Eclipse RCP
20.09.2010 - 24.09.2010, Hamburg
git
07.10.2010 - 08.10.2010, Essen
26.10.2009

Developing Eclipse RCP applications following the MVC pattern

In this blog post I’ll show how Eclipse RCP applications can be developed following the MVC pattern. I’m going to use Spring DM to connect the controllers with their model and view components. Also, Eclipse Riena ridgets are utilized to access the view from the controller. This started as proof of concept, so I’m using a very simple example.

Creating the example application

The example is just a single view part embedded in an Eclipse RCP application containing two text fields for entering numbers and a button to calculate the sum of both numbers:

I assume you know how to create such an example application. The view code looks like this:

public class CalculatorView extends ViewPart {

    public static final String VIEW_ID = "com.example.rcpmvc.calculator";

    @Override
    public void createPartControl(Composite parent) {
        Text nr1 = new Text(parent, SWT.BORDER);
        Text nr2 = new Text(parent, SWT.BORDER);
        Button calculate = new Button(parent, SWT.NONE);
        calculate.setText("Calculate");
        Text result = new Text(parent, SWT.BORDER);

        GridLayoutFactory.fillDefaults().numColumns(4).spacing(5, 3).margins(5, 5).applyTo(parent);
    }

}

I’m going to use a variant of the MVC pattern that has been termed “Passive View” because the view is entirely passive and doesn’t know a thing about the application data or logic. You can read more about the general concept in Martin Fowlers wiki: Passive View.

May I introduce: Model, View and Controller

So we have our view. The respective model represents the data and the state of the UI. For this example, it might look like this:

public class CalculatorModel {

    private int nr1, nr2, result;

    public int getNr1() {
        return nr1;
    }

    public void setNr1(int nr1) {
        this.nr1 = nr1;
    }

    public int getNr2() {
        return nr2;
    }

    public void setNr2(int nr2) {
        this.nr2 = nr2;
    }

    public int getResult() {
        return result;
    }

    public void setResult(int result) {
        this.result = result;
    }

}

The idea of MVC/Passive view is that model and view are passive. They are working in their own world, holding data and viewing views. The action happens in the controller. The controller is responsible for connecting the model and the view and for intermediating between those two worlds:

Model-View-Controller Passive View

The controller might look like this:

public class CalculatorController {

    public CalculatorController(CalculatorView view, CalculatorModel model) {
        // connect view and model here
    }

}

We will have a look at the details of the actual implementation later on.

Connecting the MVC components

So we have these three objects. Who is responsible for creating and connecting them? In an Eclipse RCP application the workbench is responsible for creating the views. Everything else has to be done after the view has been created. Also, the components should not know about their partners directly.

This can be achieved using the Dependency Injection (DI) / Inversion of Control (IoC) paradigm. A component should not look up their companion objects itself. Instead, they get their dependencies injected from the outside. The developer describes how these components are to be wired separate from the actual components. In general, this helps with keeping all the components very loosely coupled.

I always liked the Spring implementation of that general concept, because it has been proven reliable on the server-side and can also be used in an OSGi environment via Spring Dynamic Modules. Have a look at Tutorial: Spring OSGi + Eclipse RCP to learn about how to use Spring in Eclipse RCP applications. Have a look at the Spring documentation to learn about Spring IoC container in general: Spring Framework - Reference Documentation

I’m borrowing the idea of connecting the MVC components in such a way from the Agile RCP framework. Unfortunately it has been a bit quiet around that project in the last year, so I’m only using the general idea: Let’s wire the controller with the view and the model using Spring, like this:

<bean id="com.example.rcpmvc.demo.calculator"
      class="com.example.rcpmvc.demo.CalculatorController" scope="prototype">

    <property name="view">
        <bean class="com.example.rcpmvc.demo.CalculatorView"/>
    </property>

    <property name="model">
        <bean class="com.example.rcpmvc.demo.CalculatorModel"/>
    </property>

</bean>

The controller bean gets the view and the model injected. As this bean lives in the prototype scope, a fresh object is created every time the controller bean is requested. But who requests the controller bean?

We have to cheat a little bit here. The right moment to create such a controller bean is when the Eclipse RCP framework creates the view. To solve this, I created a SpringViewFactory based on Martin Lipperts SpringExtensionFactory. This factory creates the controller and hands out the respective view. I’m providing this class together with some more utility classes and an example project here: mvc_rcp.

This can be used to refer views by their controller bean, for example in view extensions:

<extension point="org.eclipse.ui.views">
    <view
        class="de.ralfebert.rcpmvc.spring.SpringViewFactory"
        id="com.example.rcpmvc.demo.calculator"
        name="Calculator"
        restorable="true"/>
    </view>
</extension>

Accessing the view components

Another question is how the controller accesses the view. It is worth pursuing to not use the SWT widgets directly. Instead one might want to use some kind of wrapper which could work even without the actual widgets. This is done mostly for testability, as writing tests using actual widgets is a cumbersome and non-trivial undertaking.

Also, the widget API is very low-level. Wouldn’t it be nice if the controller could use a bit more functionality out of the box in terms of working with the widget? (things like data binding, validation, etc.)

Of course we could write our own wrapper classes. But for some reason, I have stopped reinventing wheels and started to reuse whatever I can, even if I end up having a somewhat general purpose wheel that needs some improvements. As it turns out, the Eclipse Riena framework has exactly such wrapper classes called Ridgets. They encapsulate widgets, offer a lot of helpful API intented to be used by controller classes and will do the job for our task at hand. Also, ridgets can be used for RCP applications without buying all the other framework features. To learn more about Riena and it’s other capabilities, have a look at Eclipse Riena Tutorial.

Using the ridget classes solely without the Riena framework requires some setup work. I’m going to skip these details, as I provided some simple base classes in the mvc_rcp project for using Ridgets in Eclipse RCP applications: have a look at the code to learn what’s necessary to do this. Riena also comes with an example project called org.eclipse.riena.sample.app.client.rcpmail which shows how ridgets can be used for the RCP mail example application.

Using the provided utility classes, you can implement the calculator example easily. At first, subclass the view from PassiveViewPart. Use addUIControl or UIControlsFactory to assign IDs to the SWT widgets:

public class CalculatorView extends PassiveViewPart {

    public static final String VIEW_ID = "com.example.rcpmvc.demo.calculator";

    @Override
    public void basicCreatePartControl(Composite parent) {
        UIControlsFactory.createText(parent, SWT.BORDER, "nr1");
        UIControlsFactory.createText(parent, SWT.BORDER, "nr2");
        UIControlsFactory.createButton(parent, "Calculate", "calculate");
        UIControlsFactory.createText(parent, SWT.BORDER, "result");

        GridLayoutFactory.fillDefaults().numColumns(4).spacing(5, 3).margins(5, 5).applyTo(parent);
    }

}

In this example, the text controls are named nr1, nr2 and result, the button is named calculate.

The controller class can be subclassed from ViewController (which is also provided in mvc_rcp). Implement the configureRidget method to wire the view with the model. You can access ridgets using getRidget and use all the API from the ridget classes to setup the view:

public class CalculatorController extends ViewController<CalculatorView, CalculatorModel> {

    @Override
    public void configureRidgets() {
        ITextRidget nr1 = (ITextRidget) getRidget("nr1");
        ITextRidget nr2 = (ITextRidget) getRidget("nr2");
        final ITextRidget result = (ITextRidget) getRidget("result");
        IActionRidget calculate = (IActionRidget) getRidget("calculate");

        nr1.bindToModel(getModel(), "nr1");
        nr2.bindToModel(getModel(), "nr2");
        result.bindToModel(getModel(), "result");

        calculate.addListener(new IActionListener() {
            @Override
            public void callback() {
                getModel().setResult(getModel().getNr1() + getModel().getNr2());
                result.updateFromModel();
            }
        });

        updateAllRidgetsFromModel();
    }

}

And here is the fully featured calculator:

That’s it for this time. Of course there are more things necessary to use this in real world applications. I basically did this as proof of concept and to see if anybody is interested in this. I’m looking forward to your comments!

Patrick Paulin, 26. Oktober, 15:58 Uhr

This is great information. Thanks! I'd appreciate hearing more about your use of Passive Views, especially if you have any tips for writing controller unit tests.

By the way, you should really add your blog to Planet Eclipse. If you are already, just ignore this, but I don't think I've seen your posts there in the past.

Anyway, keep up the great work! This is really useful.

--- Patrick

Ralf Ebert, 26. Oktober, 16:10 Uhr

Thanks, I just filed a request for addition in the Planet Eclipse bugzilla.
I plan to continue this with realistic examples and controller tests. There is a Riena feature request that will make this a lot easier (#293256: Ridget testing support). This was meant just as a proof of concept to check if RCP/Spring/Ridgets can be melted together as shown.

Setya, 27. Oktober, 11:07 Uhr

Thanks for the article,

How to do the same on EditorPart instead of ViewPart ?

Setya

Ralf Ebert, 27. Oktober, 11:52 Uhr

Hi Setya,

you can do the same in principle for an editor. Just have a look at the PassiveViewPart class and create an PassiveEditorPart (or even better, contribute such a class to the mvc_rcp project ;)

Setya, 27. Oktober, 18:14 Uhr

Hi again,

I've studied the code briefly, my question is, which class invokes Controller#configureRidgets ?

Ralf Ebert, 27. Oktober, 19:37 Uhr

The controller receives an event when the view is available. This triggers the creation of the ridgets and configureRidgets is called after that. Just set a breakpoint in the configureRidget method and have a look at the call stack to find out the details.

Setya, 29. Oktober, 14:23 Uhr

Ralf,

I was thinking about implementing the same MVC pattern, only not tied to Riena, but I'm still not sure how I can listen to ViewPart being available so that I can invoke controller to do binding.

Cheers,
Setya

Ralf Ebert, 29. Oktober, 21:25 Uhr

@Setya, there is no event available for this - you need to build this yourself. Just fire an event in the ViewPart.createPartControl method after the UI has been created and let the controller listen to that event (exactly that happens in the mvc_rcp PassiveViewPart class).

Setya, 15. November, 18:50 Uhr

Hi Ralf,

Your snippet in SpringViewFactory didn't call view's setInitializationData so that the view's title is missing.

Cheers,
Setya

Ralf Ebert, 17. November, 16:10 Uhr

@Setya, thanks for the hint, I missed to forward the setInitializationData call to the view, at the moment the controller would be notified. Unfortunately that cannot be fixed easily. I'll change it to another approach of wiring this up and post an update in the next days.

Setya, 24. November, 12:07 Uhr

Ralf,

How do you obtain references to controller instance throughout your code when needed ?
I can only think of attaching it to view, but since view should know nothing about controller, this doesn't sound right does it ?

Cheers,
Setya

Ralf Ebert, 24. November, 13:46 Uhr

@Setya, as this blog post was experimental/exploratory I don't have a recipe for this problem ready yet. One idea would be to let the DI container manage the lifecycle of the components so you can get them injected (not sure how far you get with such an approach for components that can exist multiple times like editors). Also, the controllers could be tracked by an enhanced ViewFactory and made accessible using something like a ControllerRegistry.

I'm looking forward to your comments:

Schulungen

Eclipse RCP

Ralf Ebert | Blog | Eclipse RCP | Developing Eclipse RCP applications following the MVC pattern