Pregunta

Short story:

I have a class that is being used as a Part in the e4xmi. This class has a constructor, which initializes a list, and methods that are bindings from a DS (declarative service on OSGi). When the binding methods are called, this list should add or remove an item.

The first time the constructor is called (automatically by the e4, because it's a Part in the Application Model), the list is initialized. Ok, that's how it is supposed to work.

After that, an item is added to the list (because the framework calls the DS binding method automatically). But then, the constructor of Part is called again, initializing and clearing my list that had one item.

I don't know why the framework is calling the constructor twice. What am I doing wrong?

Long story:

What I want to achieve:

I've created a simple e4 RCP application for study purposes that would have a view capable of loading lots of widgets (clock, weather forecast, feeds, etc.) in runtime.

What I think I should do to achieve that:

In order to achieve that, I'm planning to have bundles that implements the same service (i.e. IWidget) and a view that looks up for that service and adds them in the layout.

What I currently have:

Currently I have 4 bundles:

  • Widget bundle, which has just a IWidget interface with a getName() method;
  • ClockWidget bundle, an implementation of IWidget;
  • MainViewPart, a Part that represents the main view. It has methods to add/remove widgets whenever they are installed in the OSGi container. I'm doing this through DS (declarative services);
  • Application, a project that contains an e4xmi file with a Part referencing the MainViewPart.

A simple class diagram can be found here to ease the explanation: enter image description here

What my issue is:

When I run the application, the MainViewPart constructor is being called twice, as you can see in the outputs:

*1. MainViewPart - Constructor
2. ClockWidget - Constructor
3. MainViewPart - Adding widget ClockWidget
*4. MainViewPart - Constructor
5. MainViewPart - @PostConstruct
6. MainViewPart - Removing widget ClockWidget

Notice that the MainViewPart constructor is being called in lines 1 and 4. I haven't seen anyone complaining about this in the web so probably I'm doing something wrong here. This strange behaviour is a terrible limitation for me. Actually I don't even know if this is a good approach to do what I'm trying to achieve.

Any help would be appreciated.

Sources:

ClockWidget.java:

package br.com.fernandopaz.rcp.test.widget.clock;

import br.com.fernandopaz.rcp.test.widget.IWidget;

public class ClockWidget implements IWidget {

    public ClockWidget() {
        System.out.println("ClockWidget - Constructor");
    }

    @Override
    public String getName() {
        return "ClockWidget";
    }
}

MainViewPart.java:

package br.com.fernandopaz.rcp.test.part.mainview;

import javax.annotation.PostConstruct;
import org.eclipse.swt.widgets.Composite;
import br.com.fernandopaz.rcp.test.widget.IWidget;

public class MainViewPart {

    public MainViewPart() {
        System.out.println("MainViewPart - Constructor");
    }

    @PostConstruct
    public void postConstruct(Composite parent) {
        System.out.println("MainViewPart - @PostConstruct");
    }

    public void addWidget(IWidget widget) {
        System.out.println("MainViewPart - Adding widget " + widget.getName());
    }

    public void removeWidget(IWidget widget) {
        System.out.println("MainViewPart - Removing widget " + widget.getName());
    }
}

Application.e4xmi:

<?xml version="1.0" encoding="UTF-8"?>
<application:Application xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:application="http://www.eclipse.org/ui/2010/UIModel/application" xmlns:basic="http://www.eclipse.org/ui/2010/UIModel/application/ui/basic" xmi:id="_T7vUMNx5EeOS5eqwaRWLag" elementId="org.eclipse.e4.ide.application" bindingContexts="_T7vUOdx5EeOS5eqwaRWLag">
  <children xsi:type="basic:TrimmedWindow" xmi:id="_T7vUMdx5EeOS5eqwaRWLag" label="br.com.fernandopaz.rcp.test" width="500" height="400">
    <children xsi:type="basic:PartSashContainer" xmi:id="_t7ieENx5EeOS5eqwaRWLag" elementId="br.com.fernandopaz.rcp.test.partsashcontainer.0">
      <children xsi:type="basic:Part" xmi:id="_uRJhsNx5EeOS5eqwaRWLag" elementId="br.com.fernandopaz.rcp.test.part.0" contributionURI="bundleclass://br.com.fernandopaz.rcp.test.part.mainview/br.com.fernandopaz.rcp.test.part.mainview.MainViewPart"/>
    </children>
  </children>
  <rootContext xmi:id="_T7vUOdx5EeOS5eqwaRWLag" elementId="org.eclipse.ui.contexts.dialogAndWindow" name="In Dialog and Windows">
    <children xmi:id="_T7vUOtx5EeOS5eqwaRWLag" elementId="org.eclipse.ui.contexts.window" name="In Windows"/>
    <children xmi:id="_T7vUO9x5EeOS5eqwaRWLag" elementId="org.eclipse.ui.contexts.dialog" name="In Dialogs"/>
  </rootContext>
  <addons xmi:id="_T7vUMtx5EeOS5eqwaRWLag" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
  <addons xmi:id="_T7vUM9x5EeOS5eqwaRWLag" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
  <addons xmi:id="_T7vUNNx5EeOS5eqwaRWLag" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
  <addons xmi:id="_T7vUNdx5EeOS5eqwaRWLag" elementId="org.eclipse.e4.ui.workbench.commands.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.CommandProcessingAddon"/>
  <addons xmi:id="_T7vUNtx5EeOS5eqwaRWLag" elementId="org.eclipse.e4.ui.workbench.handler.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.HandlerProcessingAddon"/>
  <addons xmi:id="_T7vUN9x5EeOS5eqwaRWLag" elementId="org.eclipse.e4.ui.workbench.contexts.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench/org.eclipse.e4.ui.internal.workbench.addons.ContextProcessingAddon"/>
  <addons xmi:id="_T7vUONx5EeOS5eqwaRWLag" elementId="org.eclipse.e4.ui.workbench.bindings.model" contributionURI="bundleclass://org.eclipse.e4.ui.workbench.swt/org.eclipse.e4.ui.workbench.swt.util.BindingProcessingAddon"/>
</application:Application>
¿Fue útil?

Solución

Tom Schindl answered this question in the Eclipse Forums: http://www.eclipse.org/forums/index.php/mv/msg/755949/1371017/#msg_1371017

There are 2 instances being construct because:

  • OSGi automatically instantiates one MPart because I've declared it as a service using DS;
  • e4 automatically instantiates another MPart because I've defined it as a Part in the Application Model (e4xmi).

In order to avoid that, I considered two approaches:

  • Instead of using binding methods from the DS, I called a method in the @PostConstruct method that looks up for all IWidgets:

MainViewPart.java:

private ArrayList<IWidget> widgets;

@PostConstruct
public void postConstruct(Composite parent) {
    lookupWidgets();
}

private void lookupWidgets() throws InvalidSyntaxException {
    widgets = new ArrayList<>();

    BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); 
    Collection<ServiceReference<IWidget>> serviceReferences = context.getServiceReferences(IWidget.class, null);

    for (ServiceReference<IWidget> serviceReference : serviceReferences) {
        IWidget widget = (IWidget) context.getService(serviceReference);
        widgets.add(widget);
    }
}
  • Or create another bundle to store the widgets. The MPart will receive it in the @PostConstruct method through DI (Dependency Injection).

WidgetList.java in the Widget List bundle:

public class WidgetContainer {

    private ArrayList<IWidget> widgets;

    public WidgetContainer() {
        widgets = new ArrayList<>();
    }

    public void add(IWidget widget) {
        widgets.add(widget);
    }

    public void remove(IWidget widget) {
        widgets.remove(widget);
    }

    public ArrayList<IWidget> getWidgets() {
        return widgets;
    }
}

component.xml (DS) for the Widget List bundle:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="WidgetList">
   <implementation class="br.com.fernandopaz.e4.test.widgetlist.WidgetList"/>
   <reference bind="add" cardinality="1..n" interface="br.com.fernandopaz.e4.test.widget.IWidget" name="IWidget" policy="dynamic" unbind="remove"/>
</scr:component>

MainViewPart.java in the Main View Part bundle:

private ArrayList<IWidget> widgets;

@PostConstruct
public void postConstruct(Composite parent, WidgetList widgetList) {
    widgets = widgetList.getWidgets();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top