Let us do some more basics with the “Spring Rich Client” 1.0.0 (SpringRC). Although it is time to give an overview of the entire architecture I won’t do this here, because I am a newbie with Spring and SpringRC. So we will just look into the steps how we get things running.
Update: See an older post for a good intro or this link collection for more information.
Startup
First of all we want to include a license prompt on the very first startup. If it is in pure text format surround the license with <pre>licenseText</pre>, save it to the classpath as /path/license.txt and do the following:
- Add the source code into LifecycleAdvisor
private final Log logger = LogFactory.getLog(getClass());
private static final String SHOW_WIZARD_KEY = "setupWizard.show";
private Settings settings;
@Override
public void onPreStartup() {
boolean showDialog = true;
if (settings.contains(SHOW_WIZARD_KEY)) {
showDialog = settings.getBoolean(SHOW_WIZARD_KEY);
}
if (showDialog && getApplication().getApplicationContext().containsBean("setupWizard")) {
SetupWizard setupWizard = (SetupWizard) getApplication().getApplicationContext().getBean(
"setupWizard", SetupWizard.class);
setupWizard.execute();
settings.setBoolean(SHOW_WIZARD_KEY, false);
try {
settings.save();
} catch (IOException ex) {
logger.error("Can't save state.", ex);
} } }
public void setSettings(Settings set) {
settings = set;
}
- Add the following text into the application-context.xml
<bean id="setupWizard" class="org.springframework.richclient.application.setup.SetupWizard">
<property name="licenseTextLocation" value="/path/license.txt" />
</bean>
<bean id="xmlInternalSettings"
class="org.springframework.richclient.settings.xml.XmlSettingsFactory"
factory-bean="settingsManager"
factory-method="getInternalSettings">
</bean>
<bean id="settingsManager" class="org.springframework.richclient.settings.SettingsManager">
<property name="settingsFactory">
<bean class="org.springframework.richclient.settings.xml.XmlSettingsFactory"/>
</property>
</bean>
And insert <property name=”settings” ref=”xmlInternalSettings” /> into the lifecycleAdvisor bean’s property list.
- As last step you have to fill in the message text for this wizard into the message.properties file:
setup.intro.welcomeTo = Welcome to
setup.intro.title = TimeFinder!
setup.intro.description = TimeFinder in one of its future releases will help you with automatic and manual timetabling.
setup.cancel.title=Cancel Setup
setup.cancel.message = Do you really want to exit the application?
acceptLicenseCommand.label=I &accept the terms of this license agreement
doNotAcceptLicenseCommand.label=I do ¬ accept the terms of this license agreement
setup.license.title=License agreement
setup.license.description=Please read the following license agreement carefully
The most of the xml code above is necessary to inject a new instance of Settings which will be created from the SettingsManager.
… Break …
Does anybody of you know, how I can do this in Java, not in ugly xml? Well, I could create the factory directly in the LifecycleAdvisor, but how can I do this dependency injection in Java (with Spring)?
… Okay, let us go further …
This settings object is then accessible within the LifecycleAdvisor because of the setSettings method and is necessary to save, if this wizard should be prompted on the next startup again. The xml-settings will be written to currentPath/settings/internal.settings.xml.
The used SetupWizard extends from AbstractWizard. This abstract class could be used for any other wizards: just add one or more AbstractWizardPages to the wizard and see this documentation. Here is the result:

Another cool feature is to show a progressbar on the splashscreen. This was very easy: add (or replace the existing) bean to the startup-context.xml file:
<bean id="splashScreen" class="org.springframework.richclient.application.splash.ProgressSplashScreen" scope="prototype">
<property name="imageResourcePath" value="/de/peterk/springr/ui/images/splash-screen.png" />
<property name="showProgressLabel" value="true" />
</bean>
Now I did an ugly hack. I want to change the colors of the progressbar. But only on the progressbar on startup, so we have to revert the following code:
Color myYellow = new Color(255, 225, 100);
Color myGreen = new Color(120, 208, 60);
UIManager.put("ProgressBar.selectionForeground", myYellow);
UIManager.put("ProgressBar.selectionBackground", myGreen);
UIManager.put("ProgressBar.foreground", myGreen);
UIManager.put("ProgressBar.background", myYellow);
UIManager.put("ProgressBar.border", new LineBorderUIResource(myYellow));
To revert these changes, please look into the method LifecycleAdvisor.onPreWindowOpen (see the resources section, I will not post it here – it is too stupid ;-))
Here is the result:

Views …
Hopefully I can create a next blog entry about the docking framework MyDoggy and the integration into SpringRC. Now it works like a charm – try it out! (see resources).
Today I will show what you can apply to all ‘component managers’ that are integrated in SpringRC.
First: there is one application per virtual machine. But there could be one or more ApplicationPages (JFrames) with several views (center, north, south, …). In our case (MyDoggy) there is only one ApplicationPage.
Normally you want to have more than one component per start-up page. To change this add the following xml to the application-context.xml:
<bean id="aLotOfViews" class="org.springframework.richclient.application.support.MultiViewPageDescriptor">
<property name="viewDescriptors">
<list>
<value>view2</value>
<value>initialView</value>
</list>
</property>
</bean>
And change the startingPageId in the lifecycleAdvisor bean to aLotOfViews. With the upcoming MyDoggy 1.5.0 enabled we can get this nice layout, which will be automatically saved(!):

Of course it is possible to use other layout manager like swing dockings:

… and more!
If you want to use custom components: just add them! I created a custom component with the open source Matisse GUI builder integrated in NetBeans. Here is the code how you can put this component into SpringRC:
public class CalculatorView extends AbstractView {
protected JComponent createControl() {
JPanel panel = getComponentFactory().createPanel(new BorderLayout());
panel.add(new CalculatorPanel()/*created with Matisse*/, BorderLayout.CENTER);
return panel;
}}
To add this view to the startingPage do (aLofOfViews already references to this bean, so no changes are necessary there):
<bean id="view2" class="de.timefinder.core.ui.mydoggy.MyDoggyViewDescriptor">
<property name="viewClass" value="de.timefinder.core.ui.CalculatorView" />
</bean>
One thing you have to know is: How to translate keys within this ‘external’ CalculatorView?
In Matisse do:
- Click the button, where you want to translate the text
- Then – in the property panel on the right side – click on the […] button
- Select “Resource Bundle” instead of “Plain Text”
- Use the messages.properties file for the bundle name
- Insert the appropriate key (defined in messages.properties)
- Use the following line to get the value (click on the “Format…” button) Application.instance().getApplicationContext().getMessage(“{key}”, null, null)
For all of the following components it is easier: do 1, 2, 3 and insert the key! This could be the new slogan for Matisse 😉
Now some more I18N. For example you want to display: “Result of $number!” you should use the following line in message.properties:
calculatorPanel.title=Result of {0} !
and get the formatted string via
Application.instance().getApplicationContext().getMessage("calculatorPanel.title", new Object[]{result}, null);
Another small point could be the ugly output of the standard java 1.4 Logger. We will now change the two-lined logging output into a one-lined. Actually this is a one-liner if you look for yourself into the resource section for the TFFormatter class:
java.util.logging.Logger.getLogger("de.timefinder").getParent().getHandlers()[0].setFormatter(new TFFormatter());
Tasks
In Swing you can use the Swing ProgressMonitor, but in SpringRC you should use the integrated ProgressMonitor to indicate the current progress of a task in the bottom right corner of the page:

With the factorial view of my example you can calculate, yes, factorials. I tried “50 000” and the calculation took nearly the same time as adding the number to the JTextArea …
So, use the ProgressMonitor in combination with the SwingWorker (I grabbed it from jdesktop, but you can use SpringRC’s Swingworker as well. Get the concurrent package from here or through maven. )
Now you can use the following code to execute a long running task:
ApplicationWindow aw = Application.instance().getActiveWindow();
final ProgressMonitor pm = aw.getStatusBar().getProgressMonitor();
String str = Application.instance().getApplicationContext().getMessage("calculatorPanel.startTask", null, null);
pm.taskStarted(str, -1);
SwingWorker sw = new SwingWorker() {
BigInteger resultLong = BigInteger.ONE;
//protected Object construct() in SpringRC's SwingWorker!
protected Object doInBackground() {
// now my long task and the progress indication
for (int i = 2; i <= fac && !pm.isCanceled(); i++) {
pm.worked(100 * i / fac);
resultLong = resultLong.multiply(BigInteger.valueOf(i));
}
return resultLong;
}
//protected void finished()
@Override
protected void done() {
// all of the following code will be called on the Event Dispatching Thread
if (pm.isCanceled()) {
output.setText("Canceled");
} else {
output.setText(resultLong.toString());
}
pm.done();
}
};
//sw.start();
//sw.clear(); // reuse would be possible with SpringRC's SwingWorker!
sw.execute();
Conclusion
SpringRC offers us – as client-side-programmers – a bunch of powerful utilities to build our application fast and scaleable. Some parts are well documented; some parts not. But I think with relaunching the project in October ’08 (hopefully!) as Spring Desktop it will get a lot of support from even more users and maybe from the company SpringSource itself. Hopefully then the documentation will be better…
The most important point for me is that SpringRC is ‘only’ a sweat jar collection and not a layer between me and Swing.
Another point is that SpringRC uses dependency injection and NetBeans RCP uses the service locator approach e.g.
SomeInterfaceImpl impl = Lookup.lookup(SomeInterface.class).
instead of the setter injection in the very first example for SpringRC.
For others this could be one more argument against NetBeans RCP (not for me …)
Resources
- Another older, but very good tutorial is located here.
- Maybe it is interesting to know: in the updated version of the book Spring – A Developer’s Notebook the last chapter 9 is about the Spring Rich Client Project (author was Keith Donald).
- Here you can find part 1.
- Here you can download the project without the jars included in the SpringRC’s download (It is a NetBeans 6.1 project, so you can simply copy all jars of SpringRC into the lib folder and open the project with NetBeans. Then resolve references via right click or change the references directly; in nbproject/project.properties).
- Update: Other resources can be found here.