Platinum Partner
java

Getting Even Further with Spring RCP (2)

In previous parts of this series on Spring RCP (Getting Started with Spring RCP, Getting Further with Spring RCP, and Getting Even Further with Spring RCP (1)), we covered a variety of subjects, from the initial steps of getting started, to the preliminary topics, such as views, actions, docking, and dialogs. Here we look at a more advanced topic: how to integrate existing docking frameworks into a Spring RCP application.

By default, as indicated here, two docking frameworks are supported: VLDocking and FlexDock, by means of the spring-richclient-docking package:

In the first two parts of this article, we examine how to integrate these, using the hooks that Spring RCP makes available via the classes listed above. After that, we get acquainted with Jonny Wray's https://jide-springrcp.dev.java.net/ project and then extrapolate some key lessons from that integration, as well as from Peter Karich's recent excellent Javalobby article that includes code illustrating MyDoggy integration. The extrapolation then takes Tim Boudreau's TabbedContainer component (which is part of the NetBeans Platform but can, as you will see, be used outside of it) and integrates it as a docking framework for a Spring RCP application, as a very simplistic illustration of how that might be done for any other framework.

Table of Contents


I do not claim to be any kind of Spring RCP expert, so everything described in the sections above should be taken with a bag of salt. However, in each case, the provided solution works. In each case, though, there are probably better ways of arriving at the same (or better) outcomes. I hope that those who know those solutions will chime in and correct the errors of my ways.

Note: A completed code example for each of the above scenarios is available as a NetBeans project, as part of the Spring RCP Tooling plugin in the NetBeans Plugin Portal, from version 1.5 of the plugin onwards. Open the New Project wizard (Ctrl-Shift-N) and you should find "Spring RCP Tutorial Part 4", "Spring RCP Tutorial Part 5", "Spring RCP Tutorial Part 6", "Spring RCP Tutorial Part 7", and "Spring RCP Tutorial Part 8", which are samples for VLDocking, FlexDock, JIDE, MyDoggy, and TabbedContainer, respectively.


 

VLDocking Integration

In every possible sense, the VLDocking framework appears to be the docking framework of choice for Spring RCP. That is the case, firstly, because the VLDocking JAR is part of the Spring RCP distribution and secondly because the requisite classes for integrating VLDocking with Spring RCP are a standard part of the Spring RCP distribution too. The latter point is equally true for FlexDock (which is discussed in the next section) but, as can be seen from the screenshot in the previous section, the number of integration classes provided with the Spring RCP distribution is less than for VLDocking. On top of that, FlexDock does not appear to be undergoing active development, while VLDocking 2.1.8 was released just over a week ago.

Therefore, it makes sense to deal with VLDocking first. Let's use it to create a window layout that looks as follows:

At the same time, let's make sure that any layout changes made by the user (e.g., if the user moves some of the views to different positions) are restored upon restart of the application. How to do that? Obtain the layout from an XML file, which is a scenario supported by VLDocking.

First, in the richclient-application-context.xml:

<bean id="lifecycleAdvisor" class="simple.SimpleLifecycleAdvisor">
<property name="windowCommandBarDefinitions" value="ui/commands-context.xml" />
<property name="windowCommandManagerBeanName" value="windowCommandManager" />
<property name="menubarBeanName" value="menuBar" />
<property name="toolbarBeanName" value="toolBar" />
<property name="startingPageId" value="proxyPage" />
</bean>

<bean id="proxyPage"
class="org.springframework.richclient.application.docking.vldocking.VLDockingPageDescriptor">
<property name="initialLayout">
<bean class="org.springframework.core.io.ClassPathResource">
<constructor-arg type="java.lang.String">
<value>/layouts/vldocking.xml</value>
</constructor-arg>
</bean>
</property>
</bean>

<bean id="applicationPageFactory" depends-on="serviceLocator"
class="org.springframework.richclient.application.docking.vldocking.VLDockingApplicationPageFactory">
</bean>

 

Notice line 14 above, which assumes a project structure like this:

 

Then, define the layout in the "vldocking.xml" file as follows:

<?xml version="1.0"?>
<VLDocking version="2.1">
<DockingDesktop name="proxyPage">
<DockingPanel>
<Split orientation="1" location="0.2506361323155216">
<Dockable>
<Key dockName="NewSpringView2"/>
</Dockable>
<Split orientation="0" location="0.7484536082474227">
<TabbedDockable>
<Dockable>
<Key dockName="NewSpringView"/>
</Dockable>
<Dockable>
<Key dockName="NewSpringView1"/>
</Dockable>
</TabbedDockable>
<Dockable>
<Key dockName="NewSpringView3"/>
</Dockable>
</Split>
</Split>
</DockingPanel>
<TabGroups>
<TabGroup>
<Dockable>
<Key dockName="NewSpringView"/>
</Dockable>
<Dockable>
<Key dockName="NewSpringView1"/>
</Dockable>
</TabGroup>
</TabGroups>
</DockingDesktop>
</VLDocking>

 

Do you need to be some kind of VLDocking expert to create the above layout file? Do you need some special tools to create it? Do you need any special kind of knowledge about the above XML syntax? No. Simply run the application once, with the above layout file, and then move the views around to the positions you'd like them to be in, such as something like this:

Now switch to the Files window and look at the layout file that is in the "build" folder, as shown here:

Open that file and notice that it is DIFFERENT to the file in your own source structure. It looks like this (assuming you moved the views around as shown above):

<?xml version="1.0"?>
<VLDocking version="2.1">
<DockingDesktop name="proxyPage">
<DockingPanel>
<Split orientation="1" location="0.5012722646310432">
<Split orientation="0" location="0.5030927835051546">
<Dockable>
<Key dockName="NewSpringView"/>
</Dockable>
<Dockable>
<Key dockName="NewSpringView2"/>
</Dockable>
</Split>
<Split orientation="0" location="0.5030927835051546">
<Dockable>
<Key dockName="NewSpringView1"/>
</Dockable>
<Dockable>
<Key dockName="NewSpringView3"/>
</Dockable>
</Split>
</Split>
</DockingPanel>
<TabGroups>
</TabGroups>
</DockingDesktop>
</VLDocking>

 

In other words, the "build" folder will contain the layout of the views at the point where you close the application. So you can then copy the above layout into the layout file in your project structure and, in the process, use the application itself as the designer of its own layout!

Further reading:

FlexDock Integration

Let's use the FlexDock docking framework now. FlexDock, according to its site, is a docking framework for use in cross-platform Swing applications and offers features you'd expect in any desktop docking framework such as tabbed & split layouts, drag-n-drop capability, floating windows, collapsible containers, and layout persistence. (Although, I haven't figured out layout persistence yet... can someone help?)

We'll use it to create a window layout that looks as follows:

In a nutshell, you need to implement a FlexDock "PerspectiveFactory" that supplies the initial view layout and then inject it into the "ApplicationPageFactory" bean in the richclient-application-context.xml file. Here are the related snippets from the richclient-application-context.xml file:

<bean id="lifecycleAdvisor" class="simple.SimpleLifecycleAdvisor">
<property name="windowCommandBarDefinitions" value="ui/commands-context.xml" />
<property name="windowCommandManagerBeanName" value="windowCommandManager" />
<property name="menubarBeanName" value="menuBar" />
<property name="toolbarBeanName" value="toolBar" />
</bean>

<bean id="applicationPageFactory" depends-on="serviceLocator"
class="org.springframework.richclient.application.docking.flexdock.FlexDockApplicationPageFactory">
<property name="floatingEnabled" value="true" />
<property name="defaultPerspective" value="defaultPerspective" />
<property name="perspectiveFactory">
<bean class="simple.DemoPerspectiveFactory">
<property name="dockableIds">
<list>
<value>NewSpringView</value>
<value>NewSpringView1</value>
<value>NewSpringView2</value>
<value>NewSpringView3</value>
</list>
</property>
</bean>
</property>
</bean>

 

Note: Above we're not making use of the "startingPageId" property. Instead, we'll let the "applicationPageFactory" handle all of the layout for us. Line 13 refers to this class that I created in the "simple" package:

import java.util.List;

import org.flexdock.perspective.LayoutSequence;
import org.flexdock.perspective.Perspective;
import org.flexdock.perspective.PerspectiveFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

public class DemoPerspectiveFactory implements PerspectiveFactory, InitializingBean {

private List<String> dockableIds;

public Perspective getPerspective(String perspectiveId) {

Perspective perspective = new Perspective(perspectiveId, perspectiveId);
LayoutSequence sequence = perspective.getInitialSequence(true);

String prevDockableId = null;
for (String dockableId : this.dockableIds) {
sequence.add(dockableId, prevDockableId);
prevDockableId = dockableId;
}

return perspective;
}

public void afterPropertiesSet() throws Exception {
Assert.notEmpty(this.dockableIds, "No dockable ids specified");
}

public List<String> getDockableIds() {
return dockableIds;
}

public void setDockableIds(List<String> dockableIds) {
this.dockableIds = dockableIds;
}

}

 

Further reading:

 

JIDE Integration

Integration between Spring RCP and the JIDE Docking Framework is available from https://jide-springrcp.dev.java.net/, by Jonny Wray. The example you can get from there, together with the related JARs (via sources in the form of Maven projects) is a Google Search application (no SOAP API Keys are available from Google anymore, so I couldn't try out the application's functionality, but that wasn't really the point):

I played around with the above sample and extrapolated some general principles, ending up with this simple result (look at the tabs at the bottom of the screenshot below):

Some general guidelines for those interested in this integration, i.e., these are the most important JIDE-Spring RCP tags I worked into my richclient-application-context.xml file:

  
<!--Here the "startingPageId" property is important: -->
<bean id="lifecycleAdvisor" class="simple.SimpleLifecycleAdvisor">
<property name="windowCommandBarDefinitions" value="ui/commands-context.xml" />
<property name="windowCommandManagerBeanName" value="windowCommandManager" />
<property name="menubarBeanName" value="menuBar" />
<property name="toolbarBeanName" value="toolBar" />
<property name="startingPageId" value="mainPage" />
</bean>

<!--Here the we define the "startingPageId" bean: -->
<bean name="mainPage" class="com.jidesoft.spring.richclient.docking.JidePageDescriptor">
<property name="viewDescriptors">
<list>
<value>NewSpringView1</value>
<value>NewSpringView2</value>
<value>NewSpringView3</value>
</list>
</property>
</bean>

<!--Here the we define each of the view beans: -->
<bean id="NewSpringView1" class="com.jidesoft.spring.richclient.docking.view.JideViewDescriptor">
<property name="viewClass" value="simple.NewSpringView1" />
</bean>
<bean id="NewSpringView2" class="com.jidesoft.spring.richclient.docking.view.JideViewDescriptor">
<property name="viewClass" value="simple.NewSpringView2" />
</bean>
<bean id="NewSpringView3" class="com.jidesoft.spring.richclient.docking.view.JideViewDescriptor">
<property name="viewClass" value="simple.NewSpringView3" />
</bean>

<!--Here the we declare that we want to use the JIDE Application Page Factory: -->
<bean id="applicationPageFactory" class="com.jidesoft.spring.richclient.docking.JideApplicationPageFactory" />

<!--Here the we declare that we want to use the JIDE Application Window Factory: -->
<bean id="applicationWindowFactory" class="com.jidesoft.spring.richclient.docking.JideApplicationWindowFactory" >
<property name="saveLayoutOnClose" value="true" />
<property name="doubleClickAction" ref="com.jidesoft.docking.DockingManager.DOUBLE_CLICK_TO_MAXIMIZE" />
<property name="heavyweightComponentEnabled" value="true" />
<property name="showWorkspace" value="true" />
<property name="layoutVersion" value="2" />
</bean>

<!--Some related obligatory beans: -->
<bean id="com.jidesoft.docking.DockingManager.DOUBLE_CLICK_NONE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>

<bean id="com.jidesoft.docking.DockingManager.DOUBLE_CLICK_TO_FLOAT"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>

<bean id="com.jidesoft.docking.DockingManager.DOUBLE_CLICK_TO_MAXIMIZE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"/>

 

Those are the absolute basics. The result is pretty cool and I think Jonny Wray has done a really great job. Take a look at all the properties that are available per view, for example:

However, it would simplify things, I think, if he'd make the JARs available on his site, together with the Maven projects that are already there, containing the sources.

 

 

 

MyDoggy Integration

Peter Karich recently mentioned his MyDoggy integration, while also providing a NetBeans project containing, among other things, the integration code as well as code demonstrating how to use his integration. I extracted the MyDoggy/Spring RCP integration classes and created a separate Java library for them:

Then I put the resulting JAR on my own application's classpath and used it as described below to create a simple layout like this:

Here are the relevant MyDoggy pieces in my richclient-application-context.xml. In the first bean, notice that "startingPageId" property, which points to the "startPage" bean. In the "startPage" bean, the initial layout is defined in an external layout.xml file or (if that file is not there or if it is empty) by displaying two of the views injected by the referenced beans:

<bean id="lifecycleAdvisor" class="simple.SimpleLifecycleAdvisor">
<property name="windowCommandBarDefinitions" value="ui/commands-context.xml" />
<property name="windowCommandManagerBeanName" value="windowCommandManager" />
<property name="menubarBeanName" value="menuBar" />
<property name="toolbarBeanName" value="toolBar" />
<property name="startingPageId" value="startPage"/>
</bean>

<bean id="startPage" class="org.mydoggy.MyDoggyPageDescriptor">
<!-- EITHER use this file if not empty/absent: -->
<property name="initialLayout">
<bean class="org.springframework.core.io.FileSystemResource">
<constructor-arg type="java.lang.String">
<value>resources/ctx/layout.xml</value>
</constructor-arg>
</bean>
</property>
<!-- OR use these initial views: -->
<property name="viewDescriptors">
<list>
<value>NewSpringView1</value>
<value>NewSpringView2</value>
</list>
</property>
</bean>

<bean id="applicationPageFactory"
class="org.mydoggy.MyDoggyApplicationPageFactory">
</bean>

 

As indicated in line 14, there's a layout file in XML format, which follows a MyDoggy layout format:

Just like in the case of the VLDocking integration, described earlier, when you move the views in the deployed application around, the layout.xml file is regenerated. The difference is that, while in the case of VLDocking the newly generated file was found in the "build" folder, the regenerated XML file simply replaces the original file in the current version of Peter Karich's MyDoggy integration.

Note: Simply create an empty XML file, in the location and with the name defined by your equivalent of line 14 in the richclient-application-context.xml above. Then, run the application. No views will be shown because the XML file is empty. Then open some of the views. Then close the application. Now... you will see that your XML file is filled with generated code, meaning that you don't need to know anything at all about the tags that constitute the file. You should therefore never need to touch the file at all, unless you know enough about it and want to manually tweak it in some way.

In summary, Peter Karich's MyDoggy integration is really cool. Here's hoping he'll (once he's happy with it) create a project page just like the one by Jonny Wray, so that it becomes easier to get hold of the MyDoggy JAR for integration with Spring RCP.

 

Rolling Your Own

There are several more docking frameworks than have been discussed so far and, more than likely, the future will bring even more. For those who would like to integrate one or more of these alternative docking frameworks with Spring RCP, here's a brief introduction of what you'd need to do.

As an example, we'll use the TabbedContainer component, simply because it is very simple. The TabbedContainer component was discussed several years ago on Javalobby, by Scott Delap. One can obtain it in several ways, one of which is by downloading the JDocking framework, which is an (old) fork of parts of the NetBeans Platform. Another way is to get it from the NetBeans Platform itself, of which it is still a part, and where it also has related Javadoc. I wouldn't recommend using it instead of any of the others discussed so far, however, because the alternative docking frameworks presented earlier are more sophisticated.

At the end of this section, we'll have this very simple window layout, by integrating the TabbedContainer component as our docking framework:

Of the several classes that we could extend/implement/call, we will only focus on those that deal with application-level matters for now:

  • org.springframework.richclient.application.ApplicationPageFactory
  • org.springframework.richclient.application.ApplicationPage
  • org.springframework.richclient.application.support.AbstractApplicationPage
  • org.springframework.richclient.application.PageDescriptor
  • org.springframework.richclient.application.ApplicationWindow

Only the two classes in the "docking" package below will be created in this section, to provide a really simple though clear example of how you'd do the same for any other docking framework:

Simply put, to start with you need to create this bean in your richclient-application-context.xml file, which refers to the factory you see in the screenshot above:

<bean id="applicationPageFactory" class="docking.TabbedContainerApplicationPageFactory"/>

 

...and inject it with this content:

import org.springframework.richclient.application.ApplicationPage;
import org.springframework.richclient.application.ApplicationPageFactory;
import org.springframework.richclient.application.ApplicationWindow;
import org.springframework.richclient.application.PageDescriptor;

public class TabbedContainerApplicationPageFactory implements ApplicationPageFactory {

public ApplicationPage createApplicationPage
(ApplicationWindow window, PageDescriptor descriptor) {
TabbedContainerApplicationPage page = new TabbedContainerApplicationPage(window);
return page;
}
}

 

And here is the "ApplicationPage" class that is referred to above:

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JPanel;

import org.jdocking.swing.tabcontrol.DefaultTabDataModel;
import org.jdocking.swing.tabcontrol.TabData;
import org.jdocking.swing.tabcontrol.TabDataModel;
import org.jdocking.swing.tabcontrol.TabbedContainer;

import org.springframework.richclient.application.ApplicationWindow;
import org.springframework.richclient.application.PageComponent;
import org.springframework.richclient.application.support.AbstractApplicationPage;

public class TabbedContainerApplicationPage extends AbstractApplicationPage {

private TabbedContainer tabbedContainer;
private TabDataModel tabDataModel;
private TabData tabData;
private ApplicationWindow window;

TabbedContainerApplicationPage(ApplicationWindow window) {
this.window = window;
}

//Called at startup.
//In this case our initial view will be an empty JPanel,
//which we use to initialize the TabData,
//which in turn is used to initialize the TabDataModel,
//which then is used in the initialization of the TabbedContainer,
//which is then added to the Spring RCP window's ContentPane:
@Override
protected JComponent createControl() {
JPanel welcomePanel =
new JPanel();
tabData =
new TabData(welcomePanel, /*icon: */ null, "Welcome!",
"This is a tooltip");
tabDataModel =
new DefaultTabDataModel(new TabData[]{tabData});
tabbedContainer =
new TabbedContainer(tabDataModel, TabbedContainer.TYPE_VIEW);
tabbedContainer.setShowCloseButton(true);
window.getControl().getContentPane().add(tabbedContainer);
return welcomePanel;
}

//Called whenever a new view is opened:
@Override
protected void doAddPageComponent(PageComponent pageComponent) {
//From the PageComponent, get the view's content:
JPanel content = (JPanel) pageComponent.getControl();
//From the PageComponent, get the view's display name:
String name = pageComponent.getDisplayName();
//From the PageComponent, get the view's icon:
Icon icon = pageComponent.getIcon();
//Initialize a new TabData, with the retrieved items above:
tabData = new TabData(content, icon, name, "This is a tooltip");
//Add the TabData to the TabDataModel:
tabDataModel.addTab(/*position: */1, tabData);
}

//Called for each view that is open,
//when the application closes:
@Override
protected void doRemovePageComponent(PageComponent arg0) {
}

//Called to give focus to something:
@Override
protected boolean giveFocusTo(PageComponent pageComponent) {
return pageComponent.getControl().requestFocusInWindow();
}

}

 

One tweak in line 41 above:

new TabbedContainer(tabDataModel, TabbedContainer.TYPE_EDITOR);

 

...and the code will result in a slightly different layout, together with a control on the right hand side for flipping through the open views:

Another tweak in line 41...

new TabbedContainer(tabDataModel, TabbedContainer.TYPE_TOOLBAR);

 

...and you have buttons instead of tabs for switching between the views:

One can see that, though the TabbedContainer component is quite simple (it doesn't provide the typical support common to docking frameworks, such as dock/undock, maximize/minimize, etc), it's quite useful in presenting the main aspects of docking integration with Spring RCP. The earlier integrations (VLDocking, FlexDock, JIDE, and MyDoggy) illustrate that you can go a lot further and provide features on lower levels, i.e., above we have only looked at application-level docking, while we could, in addition, provide specific features on the level of individual windows within the application, such as whether/not a window can be docked/undocked, maximized/minimized, etc. Lessons relating to these aspects can be extrapolated from the source code of the other integrations and will be discussed in another instalment of this series.

 

Conclusion

We've covered the integration of several docking frameworks with Spring RCP. VLDocking and FlexDock are officially supported by Spring RCP. In fact, the VLDocking JAR is part of the Spring RCP distribution, although FlexDock seems to not be under active development anymore. One can conclude that VLDocking is the docking framework of choice when it comes to providing a windowing system for applications built on top of the Spring RCP. However, both JIDE and MyDoggy are well represented by Jonny Wray and Peter Karich and I believe both are in a very good position to be integrated. Other frameworks, including the NetBeans Platform, via its TopComponents, should also be integratable (which I'll discuss in an article soon, hopefully), via the classes that I introduced in the final section of this article.

Spring RCP clearly presents a very versatile approach: you can pick and choose whichever docking framework works best for you and then bolt it onto you Spring RCP application without too much trouble. I hope this article has given a relatively solid introduction, with starting points for further exploration via the further reading suggestions and links within the various sections.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}