RCP Workspaces
An “Eclipse workspace” is basically a directory where all your metadata preference files are stored (Eclipse specific files, there’s a lot of them). It’s also where your projects are stored, your source files, jars, and so on. It’s useful to have all of this one one place, and Eclipse itself requires it or your workspace probably won’t load. As it’s such a useful feature, I know a lot of people have tried to implement the existing “Switch Workspace” and the rest of the “Workspace” logic into their RCP applications, but I have yet to see many successes. The issue is that it’s most likely a feature you will want to customize (and in some aspects, you have to, as the “Workspace” implementation in Eclipse is not that open), and that’s where it gets tricky and possibly messy.
In our case, we wanted the normal Eclipse workspace logic so that users could create multiple “RCP Workspaces” and load whichever they wanted at startup, but we also wanted all of our custom RCP application specific data in the same directory (along with any other files we felt like creating), and we wanted to customize the dialog where the user picks a workspace. In the end, as you have probably guessed by now, we used nearly none of the default Eclipse workspace implementation, but managed to get RCP to believe that we were, and we got all the features we required in there as well.
This article will explain how we went about doing this, and will show you (with full source code of course) how you can implement exactly the same thing. And it’s much easier than it may sound.
The Magic Parameter
Everything starts with one little command line parameter that tells Eclipse we will be in charge of specifying where the workspace goes. Obviously we need to do this very early in the application or else by the time we have a UI, Eclipse has no place to put preferences. And if we do it too late, Eclipse will exit with an exception saying that it has no workspace. The “Program Argument” that makes it possible for us to define a workspace at runtime looks like this: -data @noDefault.
If you look at your “Run Configurations” in Eclipse (“Run” menu, then “Run Configurations…”) and click the “Arguments” tab, you will probably see a few cryptic entries that Eclipse has created for you, they look like this: -os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl}. By appending our @noDefault argument, we end up with this: -os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -data @noDefault.
Here is a screenshot of how it would look when appended:
![]()
So what does that parameter do? It simply tells Eclipse not to use any default workspace location (or “OSGI instance area” as it’s called in the API) when it starts up and expects us to provide it instead. Here’s an API link to the document explaining the different Eclipse runtime options you can set (including the ones mentioned here).
Telling Eclipse The Location Manually
So, where and how do we tell Eclipse that we have a “workspace home” that we want it to use? As I mentioned before, we need to do it early. That means we need to do it in Application.java and more specifically in the public Object start(IApplicationContext context) throws Exception { method. By default this method will mostly be empty except for some Eclipse specific code (undoubtedly generated by Eclipse when you created the RCP project), but we’ll soon change that. Eclipse will expect us to set the necessary location by modifying the “InstanceLocation” on the Platform. We can get the Location by doing a one-line call like this: Location instanceLoc = Platform.getInstanceLocation();. Assuming we already have a specific workspace location in mind, we could do the following:
try { // fetch the Location that we will be modifying Location instanceLoc = Platform.getInstanceLocation(); // set location to c:\temp instanceLoc.set(new URL("file", null, "c:\\temp"), false); } catch (Exception err) { ... }When we create the URL we tell it that the protocol to use is a file. We also tell it that the host is null and that the file in question is
c:\temp(which is actually a directory of course). The lastfalsethat we pass to the set method is whether we want to lock the workspace location (which we don’t).We have now satisfied Eclipse’s needs for custom-defining a workspace, and when you start your product or RCP application, you should see that Eclipse has created a
.metadatadirectory underneath it. But as we hardcoded the path toc:\temp, this doesn’t let us “customize” it much does it? What about “Switch Workspace” functionality and asking the user what workspace they would like to pick? (or create)Asking The User
In our case, we ask users at startup what workspace we should use (Just like Eclipse does). We do various checks to see if the directory they pick is already a workspace, if it’s writable/readable, and so on. If everything is OK, we return the selected directory to the previous code exmple and put it where we used
c:\tempbefore.To make it easy, you could re-use code from the “Select workspace dialog” from Eclipse, but we want ours custom, so I’ll explain how that’s done.
Let’s create a class called
PickWorkspaceDialog, as we’ll re-use this for the Switch Workspace action later, we’ll make it with a constructor that takes a boolean that defines whether it’s the normal “startup pick workspace” dialog or if it’s the “switch workspace” dialog. As Eclipse already has a pretty nice dialog that we can re-use that lets us put an image and a message at the top, we’ll be extendingTitleAreaDialog.While we’re at it, lets create features for cloning a workspace and a combo that remembers our last selected ones as well.
As it’s a lot of code to paste into an article – and I think a lot of it is straight forward – I will simply link to the entire java class and show some selective snippets below. For starters, this class was created using layouts with the LatticeLayout layout manager (Update: Site is down, old lik was here, I’ve changed link to the direct jar download which contains javadoc and sources as well), so if you don’t want to re-code those sections, go grab it. (This is a simpler MigLayout layout manager) similar to Swing’s TableLayout. Should you not wish to use either, it should be easy to change the layouts to anything you want (GridLayout for example).
You can download the Java file here (and at the bottom of the article). I suggest you browse through it quickly before we continue as it will make it much easier to follow. Do note that this class has been optimized to “fit in one class”, as normally some of the methods found in there would undoubtedly be split up into other places. But for the making this article it was combined into one file.
Using Preferences
Normally in a RCP application we would be using an
IPreferenceStoreto save/load our preferences. But as we need to save the location of what workspace we used last – before we have access our preference store (which is saved in our not-yet-selected workspace location), we need to save/load from somewhere else. The most logical solution is to use the java Preferences API. By defining a preference location along the lines of;// create/get our preferences location based on the class private static Preferences _preferences = Preferences.userNodeForPackage(PickWorkspaceDialog.class); // lets define the keys we will be saving private static final String _KeyWorkspaceRootDir = "wsRootDir"; private static final String _KeyRememberWorkspace = "wsRemember"; private static final String _KeyLastUsedWorkspaces = "wsLastUsedWorkspaces";we can now save/load those preferences as early or late as we want. Do note that any user launching the application on the same computer will be seeing the same saved preferences, so if you need a more secure or private solution this may not be ideal for you. But for 99% of us this is fine. If you do not know how the Java Preferences API works, take a quick look at the API or the PickWorkspaceDialog.java file.
Identifying Our Workspace
When a user selects a directory, we want to check if that directory is a “Our Application” workspace directory or some other plain directory. We do this by creating an empty file when the workspace is created, so that later, if they select an existing directory, we can say if it’s a valid “Our Application” workspace directory or not. In the
PickWorkspaceDialog.javaclass you will see a method namedcheckWorkspaceDirectorythat deals with both checking if a directory already exists or not (to ask if the user wants to create a new workspace there), and it also deals with checking any existing directory to ensure it’s a workspace directory for our application. The filename is defined at the top of the class, just look forpublic static final String WS_IDENTIFIER = ".our_rcp_workspace";You will also see code that checks to ensure the user doesn’t select a subdirectory of an existing workspace directory, as that could cause an infinite recursive directory-creating loop, which is obviously bad.
Running the code from within RCP will give you a dialog that looks like this (assuming you gave it a nice image to display – the wizard image used in the screenshot is part of the Eclipse platform, so you can find most of them inside the eclipse jars):
As we need to hook it up to actual code, showing the screenshot before we do so makes it easier when I mention things as the “remember” checkbox.
Hooking It Up
Back in
Application.javawe now want to change our previous code to use the dialog to ask the user for a workspace. We also need to check if the user selected the “remember” checkbox as then we don’t show the dialog at all (unless the previously selected workspace directory can’t be found). We also want to check that the old workspace directory is still OK in terms of reading/writing etc. All in all, we want something like this (excuse the formatting, the sourcecode formatter doesn’t like large deeply nested blocks):public Object start(IApplicationContext context) throws Exception { try { // the old Eclipse generated code Display display = PlatformUI.createDisplay(); // and so on.. // get what the user last said about remembering the workspace location boolean remember = PickWorkspaceDialog.isRememberWorkspace(); // get the last used workspace location String lastUsedWs = PickWorkspaceDialog.getLastSetWorkspaceDirectory(); // if we have a "remember" but no last used workspace, it's not much to remember if (remember && (lastUsedWs == null || lastUsedWs.length() == 0)) { remember = false; } // check to ensure the workspace location is still OK if (remember) { // if there's any problem whatsoever with the workspace, force a dialog which in its turn will tell them what's bad String ret = PickWorkspaceDialog.checkWorkspaceDirectory(Display.getDefault().getActiveShell(), lastUsedWs, false, false); if (ret != null) { remember = false; } } // if we don't remember the workspace, show the dialog if (!remember) { PickWorkspaceDialog pwd = new PickWorkspaceDialog(false); int pick = pwd.open(); // if the user cancelled, we can't do anything as we need a workspace, so in this case, we tell them and exit if (pick == Window.CANCEL) { if (pwd.getSelectedWorkspaceLocation() == null) { MessageDialog.openError(display.getActiveShell(), "Error", "The application can not start without a workspace root and will now exit."); try { PlatformUI.getWorkbench().close(); } catch (Exception err) { } System.exit(0); return IApplication.EXIT_OK; } } else { // tell Eclipse what the selected location was and continue instanceLoc.set(new URL("file", null, pwd.getSelectedWorkspaceLocation()), false); } } else { // set the last used location and continue instanceLoc.set(new URL("file", null, lastUsedWs), false); } // this is the normal default code from here on out int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor()); if (returnCode == PlatformUI.RETURN_RESTART) { return IApplication.EXIT_RESTART; } return IApplication.EXIT_OK; } catch (Exception err) { ... } finally { display.dispose(); } }And that’s it. We now have a fully working and modifiable “Select Workspace” dialog that tells Eclipse where to put the metadata and we can modify any of it to our hearts content. With that all out of the way, the only thing remaining is the “Switch Workspace” action.
Switch Workspace
Eclipse has a built-in Switch Workspace action that you could access from the
ActionFactory, but as we custom implemented our workspace picker, this wouldn’t help us much. Our dialog is already set up to handle a workspace switch (pretty much all it does is show a different message after all).So, lets create an Action that will be our Switch Workspace action. It will be surprisingly short, and look like this:
public class ActionSwitchWorkspace extends Action { private Image _titleImage; public ActionSwitchWorkspace(Image titleImage) { super("Switch Workspace"); _titleImage = titleImage; } @Override public void run() { PickWorkspaceDialog pwd = new PickWorkspaceDialog(true, _titleImage); int pick = pwd.open(); if (pick == Dialog.CANCEL) return; MessageDialog.openInformation(Display.getDefault().getActiveShell(), "Switch Workspace", "The client will now restart with the new workspace"); // restart client PlatformUI.getWorkbench().restart(); } }As all necessary variables and workspace locations are handled in the
PickWorkspaceDialogclass we don’t need to do anything else than tell the picked that it is now a “Switch Workspace” dialog, and then restart the RCP application if the user pressed “OK”.Ideally we’d add this action in our
ActionBarAdvisorclass (default name isApplicationActionBarAdvisor) under the File menu somewhere.The result when run will look like this:
That’s It
With the code mentioned in this article you now have full “workspace” functionality for your Eclipse RCP application without having to dig in to extension points or other XML-based overrides, and you have the ability to customize any of it as much as you want. As you see it doesn’t take that much code as most of the big code is methods for copying files and checking directories.
All Code
Here are download links to all code referenced in this article:
(For the
Application.javacode please copy the big block in the article as it shows all code related to that class.)
- Glazed Lists + SWT Tables == True If you haven’t heard about Glazed Lists until now, it’s...
- MenuManagers and You If you’re using Eclipse RCP (or even standalone SWT with...
- The Secondary ID In many RCP applications your views (ViewParts) will not be...
January 12, 2009 • Tags: Articles, Eclipse Articles, JFace • Posted in: Articles, Eclipse Articles, Programming • 14 Comments •Print This Post



14 Responses to “RCP Workspaces”
ElvisFromHell - January 27th, 2009
Nice post!
But I still have the problem that the plugins org.eclipse.osgi, org.eclipse.equinox.app and org.eclipse.core.runtime create an own folder containing “.fileTableLock” (among others) in the rcp application’s installation “configuration” subdirectory.
This is very inconvenient when running over network.
Do you have an idea how to solve this?
Jack - April 1st, 2009
Great stuff, thanks!
@ElvisFromHell – I think that simply ensuring that users do not have write privileges to the application folder will cause RCP to use their home folder for this data.
zx’s diatribe » Shirts and Workspaces - May 18th, 2009
[...] top of all this fun t-shirt selection, I’d like to point people to an excellent post by Emil Crumhorn discussing how to add the workspace chooser dialog in an RCP application. [...]
Jeff - August 6th, 2009
Nice article, thanks.
LatticeLayout link broken, any ideas?
Emil - August 6th, 2009
Thanks for the heads up, seems LatticeLayout is gone. I’ve updated the post with a direct link to the jar that I uploaded to Hexapixel (which contains sources and javadoc too). Here’s the same link: LatticeLayout Jar. I guess it’s MigLayout from here on out.
Jeff - August 6th, 2009
Wow, an author who cares!
Thanks for the jar. I’ve missed TableLayout,
but MigLayout is becoming more familiar over time.
Great, I have my own workspace(s), thanks.
Now on to custom projects and the CNF.
Erik - February 17th, 2010
Great! This works fine and it’s very easy to understand. Thanks!
Paul - July 11th, 2010
Really great stuff, helped a lot and was nice to read.
Piotr - July 26th, 2010
Very good article. Thank you. Now I’m trying to set -data @noDefault through some config file (e.g. config.ini) but I can’t get it working. Do you have any idea how to do it? I tried osgi.instance.area=@noDefault but it doesn’t work.
Ken - August 20th, 2010
Thank you so much for posting this article. I’ve been struggling with this for about a week and had about 80% of what you have here worked out, but that last 20% was killing me. Again, thank you for sharing your work!
Emil - August 20th, 2010
Glad it helped
Kaushik - March 9th, 2011
Nice article which is simple to understand, and it works too
Thanks a lot…
Adam - August 30th, 2011
Thanks for the great article! It is always good to find a complete implementation for something when you are only looking for a starting point.
The only change that I made was to add another preference value that remembers if the app is restarting from a workspace switch. In the Application.start code, if that value is true then I reset it and set the remember variable to true so that the user does not get prompted for a workspace right after they already set the new one
Matt - April 10th, 2012
Thanks for the article! I was able to reuse Eclipse’s Workspace dialog instead of using another dialog. Here’s code snippet from my Application.java file:
import org.eclipse.ui.internal.ide.ChooseWorkspaceData;
import org.eclipse.ui.internal.ide.ChooseWorkspaceDialog;
public class Application implements IApplication {
public Object start(IApplicationContext context) {
Display display = PlatformUI.createDisplay();
Location instanceLoc = Platform.getInstanceLocation();
boolean isWorkspaceSelected = false;
try {
URL url = new File(System.getProperty(“user.home”), “workspace”).toURI().toURL();
ChooseWorkspaceData data = new ChooseWorkspaceData(url);
ChooseWorkspaceDialog dialog = new ChooseWorkspaceDialog(display.getActiveShell(), data, true, true);
dialog.prompt(true);
String selection = data.getSelection();
if (selection != null) {
isWorkspaceSelected = true;
data.writePersistedData();
url = new File(selection).toURI().toURL();
instanceLoc.set(url, false);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
//removeHelpExtension();
if (isWorkspaceSelected) {
int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor(display));
if (returnCode == PlatformUI.RETURN_RESTART) {
return IApplication.EXIT_RESTART;
}
}
return IApplication.EXIT_OK;
} finally {
display.dispose();
}
}
}