Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » scout » Dependency Injection + Eclipse Scout?
Dependency Injection + Eclipse Scout? [message #1412881] Wed, 27 August 2014 09:29 Go to next message
Jeremie Bresson is currently offline Jeremie BressonFriend
Messages: 1250
Registered: October 2011
Senior Member
I am curious to know if someone on this forum has experimented Dependency Injection (DI) with Eclipse Scout.

For those who need a pointer on those topics (Dependency Injection, Contextualized Dependency Lookup, Setter Dependency Injection, Constructor Dependency Injection, Field Injection, getter Injection) read this: Inversion of Control History

With the SERVICE construct in Eclipse Scout we do dependency lookup:
SERVICES.getService(MyService.class);

The service registry is based on top of OSGi (plugin mechanism offered by the eclipse stack).

As a consequence, when we want to write Unit Tests with fake services (mock), we need to have the Scout Framework Layer up and running. This induces two consequences for the tests:
- They need to use ScoutClientTestRunner or ScoutServerTestRunner as runner (JUnit @RunWith)
- The execution of those tests is not as simple as it could be ("Run As -> JUnit Test" is not possible. You need a Test Application, a Maven+Tycho Build or "Run As -> JUnit Plug-in Test" with another configuration the unusable default).

Recently I have seen a team using this construct to test their server-services as plain JUnit Test.

If MyService uses AService and ISqlService the references are stored in private member. The service provides 2 constructors: one without any argument and one that sets the members.

public class MyService extends AbstractService implements IYService {

  final AService aService;
  final ISqlService sqlService;

  public MyService() {
    this(SERVICES.getService(AService.class), SERVICES.getService(ISqlService.class));
  }

  public MyService(AService aService, ISqlService sqlService) {
    super();
    this.aService = aService;
    this.sqlService = sqlService;
  }

  //implementation
}


These allow writing plain JUnit tests where the second constructor is used to pass mock services instead of the real implementation...

A sort of manual Dependency Injection...

I am not sure of the impact of server boot time (because when MyService is instantiated, it will do the lookup of all services it uses). The whole dynamic aspect of services is also lost (but who is using this feature anyway? The only use case I know is also for test cases).

If you do so, be careful: services in scout are instantiated only ones (singletons). This works with services but not with other Scout construct like ServerSession (ServerSession.get() will provide the object corresponding to the user executing the request).


In any case, I am interested in your opinion on this topic.

.
Re: Dependency Injection + Eclipse Scout? [message #1413234 is a reply to message #1412881] Thu, 28 August 2014 05:52 Go to previous messageGo to next message
Urs Beeli is currently offline Urs BeeliFriend
Messages: 567
Registered: October 2012
Location: Bern, Switzerland
Senior Member
My first reaction was: "But doesn't OSGi tell us never to store a service reference". Of course your point about dynamically changing services is right, so it's probably more of a philosophical reaction than one with real life impact Smile

My second thought was: "This is a pretty cool workaround", this might be interesting for us, too!
Re: Dependency Injection + Eclipse Scout? [message #1413283 is a reply to message #1412881] Thu, 28 August 2014 07:56 Go to previous messageGo to next message
Erich Steiger is currently offline Erich SteigerFriend
Messages: 14
Registered: September 2013
Junior Member

It is correct that, OSGI Services shouldn't be stored localy. This is a Workaround and it has some limitations. It works, but it is also dangerous.

A better solution could be if SERVICE can be feed with mock services in JUnit setup and then run the code as normal JUnit Test.

@Before
public void setUp() {
  SERVICE.add(new MyMockService());
}


and then in the productive code the usual way to access services can take place.

SERVICE.getService(IMyService.class);


Another solution can be a ServiceProvider which is injectable. Then it is possible to use a custom Test Service Provider in JUnit Context or the real ServiceProvider for production.
Re: Dependency Injection + Eclipse Scout? [message #1413375 is a reply to message #1413283] Thu, 28 August 2014 11:28 Go to previous messageGo to next message
Jeremie Bresson is currently offline Jeremie BressonFriend
Messages: 1250
Registered: October 2011
Senior Member
Erich Steiger wrote on Thu, 28 August 2014 09:56
A better solution could be if SERVICE can be feed with mock services in JUnit setup and then run the code as normal JUnit Test.


Exchanging the implementation returned by SERVICE.getService(..) is possible with TestingUtility.registerServices(..).

You are right this does not work as plain JUnit test, because OSGi is requested to run the test.

.
Re: Dependency Injection + Eclipse Scout? [message #1414084 is a reply to message #1413375] Sat, 30 August 2014 07:24 Go to previous messageGo to next message
Jeremie Bresson is currently offline Jeremie BressonFriend
Messages: 1250
Registered: October 2011
Senior Member
We discussed with Erich how the correct pattern would looks like, here an improved version:

You need a provider class that is responsible for doing the service call (SERVICES.getService(..)) when it is invoked. The interface could looks like this:
import org.eclipse.scout.service.IService;

public interface IScoutServiceProvider<S extends IService> {

  /**
   * @return the service instance
   */
  S getService();

}


(Side note: if you use Java 8, you recognize a SAM type (Single Abstract Method interface), which means that this class could be a lambda expression... But I develop this example with the Java6 constructs).

A possible implementation could be:
import org.eclipse.scout.service.IService;
import org.eclipse.scout.service.SERVICES;

public class ScoutServiceProvider<T extends IService> implements IScoutServiceProvider<T> {

  private final Class<T> m_serviceInterfaceClass;

  private ScoutServiceProvider(Class<T> serviceInterfaceClass) {
    m_serviceInterfaceClass = serviceInterfaceClass;
  }

  @Override
  public T getService() {
    return SERVICES.getService(m_serviceInterfaceClass);
  }

  public static <S extends IService> IScoutServiceProvider<S> newProvider(Class<S> serviceInterfaceClass) {
    return new ScoutServiceProvider<S>(serviceInterfaceClass);
  }

}


This way MyService looks like this:
MyService will use the provider and store them instead of the services references. Notice the empty constructor of the service. In the service, where you were using "SERVICE.getService(AService.class)" the provider should be used "m_AServiceProvider.getService()"
public class MyService extends AbstractService implements IMyService {

  final IScoutServiceProvider<AService> m_AServiceProvider;
  final IScoutServiceProvider<ISqlService> m_SqlServiceProvider;

  public MyService() {
    this(ScoutServiceProvider.newProvider(AService.class), ScoutServiceProvider.newProvider(ISqlService.class));
  }

  public MyService(IScoutServiceProvider<AService> aServiceProvider, IScoutServiceProvider<ISqlService> sqlServiceProvider) {
    super();
    this.m_AServiceProvider = aServiceProvider;
    this.m_SqlServiceProvider = sqlServiceProvider;
  }
  
  //...
  
  @Override
  public void doSomething() throws ProcessingException {
    AFormData formData = new AFormData;
    formData = m_AServiceProvider.getService().load(formData);
    //...
  }
}


For your tests, you can have a small utility method, creating a simple provider, that do not call SERVICE.getService(..) but return what will be your mocked implementation:
public static <T extends IService> IScoutServiceProvider<T> createProvider(final T serviceImplementation) {
  return new IScoutServiceProvider<T>() {

    @Override
    public T getService() {
      return serviceImplementation;
    }

  };
}


Your JUnit tests will looks like this:
AService aServiceMock;
ISqlService sqlServiceMock;

//Initialize the mock (with Mockito for example, or using plain Java if you prefer).

MyService serviceUnderTest = new MyService(createProvider(aServiceMock), createProvider(sqlServiceMock));

// execute tests on your service instance.


Of course, if you do not like the anonymous class approach, you can use a real class doing the same job. (or a lambda if you are on Java 8)

This pattern is absolutely correct. I think it will have only a small impact on the performance (I think the statement is inlined by the Just in Time compiler (JIT) of the JVM). You just have a lot of boilerplate code, making your Services a little bit verbose.

I think the pattern we have defined is exactly what a Dependency Injection Framework would do (sort of manual DI). A DI framework is doing the exact same thing, without writing the lines I have described here (at runtime (like with Guice) or at compile time (like with Dagger 2)).

@Urs: Thank you for your feedback. It is good to know how many projects based on Scout are interested by DI. If you want to use this pattern in your application, this second pattern is the correct one.

.

[Updated on: Sat, 30 August 2014 07:34]

Report message to a moderator

Re: Dependency Injection + Eclipse Scout? [message #1415124 is a reply to message #1414084] Tue, 02 September 2014 06:33 Go to previous message
Andre Wegmueller is currently offline Andre WegmuellerFriend
Messages: 32
Registered: September 2012
Location: Baden-D├Ąttwil, Switzerla...
Member
In our project we use Scout together with Google Guice. We've made some small changes to the following Scout classes which enables DI support for Scout services and sessions:

    * ClientProxyServiceFactory
    * ClientServiceFactory
    * ClientSessionRegistryService


For instance, the Scout's default class ClientServiceFactory creates a new instance of a Service class in this way:

private void updateInstanceCache(ServiceRegistration registration) {
  ...
  m_service = m_serviceClass.newInstance();
  if (m_service instanceof IService) {
    ((IService) m_service).initializeService(registration);
  }
  ...
}


In order to use DI with Google Guice we must create an instance of a class with the Guice injector. That's why our DIClientServiceFactory does this instead:

private void updateInstanceCache(ServiceRegistration registration) {
  ...
  m_service = ClientDI.getInstance(m_serviceClass); // required to perform DI on new instance
  if (m_service instanceof IService) {
     ((IService) m_service).initializeService(registration);
  }
  ...
}


ClientDI looks like this. Basically all it does is to provide access to the Guice injector and the module which contains the DI configuration.

final class ClientDI {

  private ClientDI() {
  }

  private static Injector injector = Guice.createInjector(new ClientDIModule());

  static <T> T getInstance(Class<T> clazz) {
    return injector.getInstance(clazz);
  }

}


In our plugin.xml we must reference the DIClientServiceFactory with a higher ranking than Scout's default service factory:

<service
    createImmediately="false"
    class="com.foo.client.util.DIClientServiceFactory"
    ranking="1000">
  </service>


Now all your services are created with DI and all dependencies are injected by the DI framework, according to your module configuration. An example from our Guice module, to provide a Scout service looks like this:

@Provides
public IDeviceService getDeviceService() {
  return SERVICES.getService(IDeviceService.class);
}


In our productive code, a class which has a dependency to the IDeviceService looks like this:

public class CardTerminal implements ICardTerminal {

  @Inject
  public CardTerminal(IDeviceService deviceService, IFooConfig fooConfig) {
    this.deviceService = deviceService; // a Scout service
    this.fooConfig = fooConfig; // a non-Scout class instance managed by the DI framework
  }
  
  ...

}


In our code we never call SERVICES.getService() directly. Instead, all services are injected as you've seen in the example above. When you're working with unit-tests, you don't need the DI framework at all. You simply pass a mock-object that implements IDeviceService to the CardTerminal CTOR.

We've also created a DIFormFactory, which creates instances of Scout forms with DI. Important: when you do this, you should no longer create form instances with Java's "new" operator (new MyForm()), instead you must always create forms by using the DI form factory, otherwise you'd simply bypass the DI injector.

A nice side-effect of this approach is that large parts of our code can be tested with plain Java unit-tests, only some tests require plug-in based test with a running OSGI / Scout framework.
Previous Topic:Fill table with data in form
Next Topic:execStore() and Confirmation
Goto Forum:
  


Current Time: Sun Dec 17 08:14:18 GMT 2017

Powered by FUDForum. Page generated in 0.02669 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software