2

I want to solve the following problem and need advice, what the best solution is.

I have a bundle A in which a service interface X is defined. A bundle B provides a service implementation of X and contributes the implementation to the tool. A and B use Google Guice and Peaberry to configure the setup of the objects.

There are two possibilities I can use to contribute the service implementation:

  1. Using an eclipse extension: In this solution I can use the GuiceExtensionFactory mechanism of Peaberry to create the service implementation using Guice and therefore can inject stuff needed by the implementation. The disadvantage here is that in the bundle defining the extension point, I need the boilerplate code for the resolution of the extensions because there is to my knowledge no way to get the extensions injected into the class which uses the extensions. This looks like this:
<extension point="A.service.X">
  <xservice
    ...
    class="org.ops4j.peaberry.eclipse.GuiceExtensionFactory:B.XImpl"
    .../>
</extension>
<extension
  point="org.ops4j.peaberry.eclipse.modules">
  <module
    class="B.XModule">
  </module>
</extension>

but I need the boilerplate code like this:

private List<X> getRegisteredX() {
    final List<X> ximpls = new ArrayList<>();
    for (final IConfigurationElement e : Platform.getExtensionRegistry().getConfigurationElementsFor(               X_EXTENSION_POINT_ID)) {
        try {
            final Object object = e.createExecutableExtension("class"); //$NON-NLS-1$
            if (object instanceof X) {
                ximpls.add((X) object);
            }
        } catch (final CoreException ex) {
            // Log
        }
    }
    return ximpls;
}
  1. Using an OSGI service: My main problem here is to ensure that the service is registered. I want the bundle loaded lazily, so at least an access to one of the classes of the bundle is required. Registering the service programmatically using Peaberry has an issue, because nobody ever asks for a class of the bundle. The solution is to provide the service as a declarative service, but I do not know a way to create the service implementation in a way, that I can use Guice to inject required objects.

So I have some questions:

  1. Is there something I do not know so far that implements the code needed to read the extensions at an extension point generically and allows to inject the extensions to the class using the extensions?
  2. Is there a way to ensure that the service is provided even if it is added using the standard Peaberry mechanism, i.e., the bundle is activated when the service is requested?
  3. Is there a way like the GuiceExtensionFactory for declarative services, so that the creation of the service implementation can be done by the injector of the bundle? Something that look like:
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Ximpl">
   <implementation class="some.generic.guiceaware.ServiceFactory:B.Ximpl"/>
   <service>
      <provide interface="A.X"/>
   </service>
</scr:component>

Summarized, I want a service implementation generated by Guice and I want to get the service implementations simply injected into the classes using the service without extensive boilerplate code. Has anybody a solution for that?

Sorry, to ask, but I searched the web for quite a while and so far I did not find a solution.

Thanks and best regards, Lars

Lars
  • 81
  • 1
  • 6

1 Answers1

2

I found a solution, but since I did not find it without a lot of trying out and thinking I thought I share it here. From the options I mentioned in my posting, my solution uses the first one, that is Eclipse extension points and extensions. In order to use Guice in the context of extension points there are two aspects to consider:

Providing an extension that is created by an Guice injector

This is explained very well here: https://code.google.com/p/peaberry/wiki/GuiceExtensionFactory. There is one remark to make from my side. The creation of the extension object is done in an injector inside of the GuiceExtensionFactory, so it is an own context, which needs to be configured by the module given as additional extension to the factory. This can become an issue, if you have other needs that require creating the injector in the bundle on your own.

Defining an extension point so that the extensions are simply injected into the classes which use the extensions.

First thing to do is to define the extension point schema file as normally. It should contain the reference of an interface that has to be implemented by the extensions.

The id of the extension point has to be connected to the interface which is provided by the extensions and which is injected by guice/peaberry. Therefore peaberry provides an annotation to be used to annotate the interface:

import org.ops4j.peaberry.eclipse.ExtensionBean;

@ExtensionBean("injected.extension.point.id")
public interface InjectedInterface {
...
}

On some web pages you also find the information that if the id is equal to the qualified name of the interface, it can be found directly without the annotation but I did not try this out.

In order to enable the injection, you have to do two things to configure the Guice injector creation.

First the EclipseRegistry object of Peaberry has to be set as ServiceRegistry. Second the binding of the extension implementations to a provided service has to be done.

The injector creation has to be done in this way:

import org.osgi.framework.BundleContext;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.ops4j.peaberry.eclipse.EclipseRegistry;

import static org.ops4j.peaberry.Peaberry.*;

void initializer() {
    Injector injector = Guice.createInjector(osgiModule(context, EclipseRegistry.eclipseRegistry()), new Module() {
               binder.bind(iterable(InjectedInterface.class)).toProvider(service(InjectedInterface.class).multiple());
        });
}

The extension implementations can then simply be injected like this:

private Iterable<InjectedInterface> registeredExtensions;

@Inject
void setSolvers(final Iterable<InjectedInterface> extensions) {
    registeredExtensions = extensions;
}

With the described way it is possible to have injected extensions which are implemented by classes using Guice to get dependencies injected.

I did not find a solution to use osgi services so far, but perhaps there is someone who has an idea.

Best regards, Lars

Community
  • 1
  • 1
Lars
  • 81
  • 1
  • 6