Community
Participate
Working Groups
Currently there exists no official RCP-API to save and restore the workbench state (on a per page basis) programmatically. This problem was discussed in the eclipse.platform.rcp news group in article "Closing and reopening IWorkbenchPage's during runtime". A typical use case, which would need this kind of controllability, are login-based RCP's which would like to save the workbench state before login attempts are taken and to restore the state after successful logins (This would allow them to save the workbench state user-specifically). Note that I don't speak here of so-named *primary* logins (during RCP startup) and *final* logouts (during RCP shut down), which already have the described behaviour, I mean login changes during runtime, which act on an already constructed workbench.
Created attachment 19082 [details] helper class to expose internals as API Comments from Nick Edgar: The proxy methods in WorkbenchStateHelper are fine for experimentation, but I would not want that to be exposed as the API. How will this be exposed to the application via the advisors, and what extra API will be needed to override the default save/close/restore behaviour?
I was expecting the helper class to be sufficient for an initial pass at this. This would be sufficient for Lotus applications. Another approach would be to apply some strategy or callback pattern to the WindowConfigurer, passing a StateHandler callback. The callback object would specify which level of state to save. For now we're looking at just Workbench/Window/Page. Thoughts?
WorkbenchStateHelper as provided is not proper API. It's in an internal package, and even if it wasn't, we probably don't want to expose this as API to regular plug-ins, just the application driving the workbench. The advisor and configurer APIs are the right place for this kind of thing. I'm thinking of something like the following call chain: Workbench.shutdown() WorkbenchAdvisor.saveState(IMemento) IWorkbenchConfigurer.saveState(IMemento) // calls current Workbench.saveState for each window, WorkbenchWindowAdvisor.saveState(IWorkbenchWindow, IMemento) IWorkbenchWindowConfigurer.saveState(IWorkbenchWindow, IMemento) // calls current WorkbenchWindow.saveState for each page WorkbenchWindowAdvisor.saveState(IWorkbenchPage, IMemento) IWorkbenchWindowConfigurer.saveState(IWorkbenchPage, IMemento) // calls current WorkbenchPage.saveState So the app's advisors can intercept the saveState calls, and can call the matching configurer method to get the default behaviour. Restore would work similarly. If you're not at the point of being able to provide advisor-level API for 3.1, that's OK, and you can patch in something like WorkbenchStateHelper for your own use.
I now have something very similar to comment #3. However, instead of baking the saveState API into the WorkbenchXXXAdvisor/XXXConfigurer I have a setter method on WorkbenchConfigurer "setSaveRestoreHandler(ISaveRestoreHandler)". The handler is then used throughout Workbench/WorkbenchWindow to save/restore state. I will change my implementation to what you have suggested. Not sure I'll have the time to tackle page level save/restore.
Just to clarify, Nick, the memento objects passed into the saveSate methods are already populated with state data? Or are you imagining adding something like "getState" to IWorkbench/IWorkbenchWindow/IWorkbenchPage that an advisor would need to call in order to save state.
No, the saveState methods do the saving. So for example, just for the workbench-level case: Workbench.shutdown() WorkbenchAdvisor.saveState(IMemento) - memento is empty at this point - call the default save method: IWorkbenchConfigurer.saveState(IMemento) - the memento now has the saved workbench state - return Likewise for windows and pages.
There is some difficulty exposing the save/restore of a perspective, for one there is no public API to pass through the advisor to the WindowConfigurer to do a default restore. Something like IPerspective would have to be introduced. Second, there is little to no information available about a perspective until the perspective.restoreState() has been completed, thus there is no valuable information to pass through to the advisor to indicate which perspective is actually being restored before it's restored. One possible workaround to this is to build a proxy memento, use that in the WindowConfigurer.restoreState(memento) call and parse the memento for information about the perspective. I don't think anyone will like this approach. We could do this internally and populate some proxy IPerspectiveDescriptor with the information. Again, I don't think that would be acceptable for 3.1, Nick please correct me if I'm wrong and I'll go ahead and implement. I think this is a fundemental problem similar to the one Nick faced at the end of bug #83658 I suggest we support only Workbench and WorkbenchWindow level save/restore. Thoughts?
If finer granularity than window is not needed for you or Daniel's scenarios, then let's limit it to that for 3.1. Otherwise, we should just defer to 3.2. Matt, I'll need to review the patch today if it's going to go into tomorrow morning's warmup build.
Created attachment 19129 [details] preview Nick here is a preview of my patch. Still need to clean up the Javadoc.
Created attachment 19133 [details] final patch candidate Same as previous patch 19129 just cleaned up Javadoc.
Matt, can you provide an example, modeling how this will used for the LWP use case? Also, all new API needs @since 3.1 tags.
In particular, I don't see how this helps you decouple saving/restoring window state from the workbench save/restore, since there's no way of saying "create a new window, restored from this memento". Or is that a different problem? I guess I need to be clearer on the motivation for this patch. Daniel, will the patch as provided address your use case?
Created attachment 19159 [details] Demonstrates how to leverage patch Nick, here is a patch which demonstrates one way an RCP application can benefit from this patch. 1) Run the patched browser demo with "-role someOne" change the layout, shutdown. 2) Then run as "-role someTwo" change the layout to something different than the layout above and shutdown. 3) Run as "someOne" again and you will get the state of the last time the workbench ran with that role. This isn't a fully polished demo, the first time a new role is run he gets the state of the previous role. That can be fixed by nuking the real workbench state file or something better. But you are correct in that the primary Lotus use case is one of a multiplexed WorkbenchAdvisor. Windows are created on demand and state cannot be pushed to those windows without using internals even with this patch. I was imagining that as the Advisor APIs evolved to support multiple WindowAdvisors per WorkbenchAdvisor the API proposed in this patch with evolve as well.
I see the patch meeting the following requirements as outlined by Daniel: A) Allow RCP apps to save the workbench state user-specifically. But does not yet satisfy this requirement: B) I mean login changes during runtime, which act on an already constructed workbench. This will require new API to allow constructing WorkbenchWindows with a specified memento. Do we have time to get this in?
I think it's worth doing B if we can. We're supposed to be API frozen today, but I'll extend this to Monday if you can tweak the patch, and ideally provide another example showing B. I'm assuming Daniel cares mostly about IWorkbenchWindow granularity, not IWorkbenchPage, although the UI would be smoother if the same window could be reused. Thanks.
API note: need to avoid referring to internal classes in API Javadoc. IWorkbenchWindowConfigurer.restoreState refers to WorkbenchWindow. We tend to use generic terms instead of type names in the Javadoc, unless it's ambiguous. E.g. "workbench" instead of "Workbench", "workbench window" instead "WorkbenchWindow".
WorkbenchWindowAdvisor.saveState and restoreState do not need to pass the IWorkbenchWindow, since the advisor was created for a specific window (obtainable via getWindowConfigurer().getWindow()). The default implementations currently ignore this argument due to the redundancy.
Created attachment 19165 [details] updated based on feedback
Created attachment 19166 [details] updated demo based on revised patch
Thanks Matt. The example patch is interesting. It saves a separate workbench memento per user, but the default behaviour of writing to workbench.xml still occurs. The new API does not let you override where the workbench state is saved, it just enables saving an extra copy, and restoring from that instead of the default workbench.xml (although the workbench.xml is still read). Due to this, if you run the example with a previously unknown user, it gets the workbench state from the last user. This is probably not what you want for LWP <g>. It seems like we should allow overriding the location of the file, in addition to being able to intercept and augment the mementos. Also, are you actually looking into B?
Created attachment 19175 [details] Updated patch, solves A and B from comment #14 This introduces one new API on IWorkbench. Probably obseletes the nees for save/restore API on WorkbenchAdvisor and IWorkbenchConfigurer. Demo to follow.
Created attachment 19176 [details] Updated patch, solves A and B from comment #14 This introduces one new API on IWorkbench. Probably obseletes the nees for save/restore API on WorkbenchAdvisor and IWorkbenchConfigurer. Demo to follow.
Created attachment 19178 [details] Demonstrates how to leverage patch to solve B from comment 14 1) Launch patched browser demo, you wont see a window, only a systemtray icon 2) Right click the icon and launch application one and change the layout to something unique. NOTE dont application one. 3) Right click again on the icon and launch application two and change the layout to something unique. 4) Now take turns closing and launching applications one and two (always keep at least one application/window open) and note that the state for each application is remembered. The patch to the workbench is not final. Some Javadoc still needs updating and the API on WorkbenchAdvisor and IWorkbenchConfigurer still need to be validated as useful.
"NOTE dont application one. " should read "NOTE dont close application one."
Matt, are you available to chat? I'm on ST.
This support for B looks good, and the example is cool. However, I think we need to clarify what problem(s) we're trying to solve here. The ones on the plate are: 1) Maintaining and switching between different states for different users 1a) for the whole session 1b) within a session 2) Allowing windows to be saved and reopened independently of the workbench session, for the same user. 3) Letting advisors tack on their own workbench-level or window-level state I think we can achieve (2) and (3) for 3.1, but not (1). The reason being is that there is lots of per-user state in the instance area besides just the workbench layout: preferences, dialog settings, random plug-ins saving stuff in the metadata area (e.g. the workspace if using org.eclipse.core.resources). Supporting multiple users with the same eclipse install can currently only be done by prompting for the instance area when the application starts. It may be possible for the app to restart the workbench with a different instance area after it exits, but even that is dicy because plug-ins often only save their state when they're being stopped. Matt and Daniel, can you please clarify which cases you really care about, or comment if I've missed a case.
Re comment 22, the method to create and restore a window from a memento should go on IWorkbenchConfigurer, not IWorkbench it should not be exposed to regular plug-ins.
Agree with comment #27 and will update patch. I think an "on demand" API like recordWindowState(IMemento) and restoreWindowState(IMemento) might be better (at least for Lotus) than baking the API into the life cycle methods. This is more inline with my helper approach. Protecting access to this API from regular plugins is the right approach. Re. comment #26, our use case is not that of multiple users sharing the same instance area. Instead we have one user with multiple applications/windows sharing the same process and instance area. An application here is just another workbench window on the same workbench instance. This is done by multiplexing the advisor. My previous patch is a crude demo of this multiplexing.
Created attachment 19191 [details] Updated patch to check against head, This patch introduces a simpler API that allows RCP applications to record the state of any IWorkbenchWindow instance at any time. Updated demo patch to follow
Created attachment 19192 [details] updated demo based on revised patch
I like this version. I've tried out the patches with the demo, works well. I'm checking this in to HEAD for M6. I have not released the example. I think it might be a good idea to consider whether restoreWorkbenchWindow(IMemento) should show the window, this might not be the desired behaviour. Matt, could you also prepare a couple JUnit tests for these two new methods?
Again, would be nice to know if Daniel (original bug reporter) could indicate whether this solves his needs as well. Leaving open at least to see if we hear something next week (before April 1st).
I will provide a JUnit test for the new API soon. Thanks.
It seems like this will address scenario 2 but not 3 (see comment 26), which I thought was important to Matt (we discussed this over Sametime). With the latest patch, there's no way for the advisor to save its own state, either at workbench or window level, for a regular workbench save. Also, I'd prefer to see the recordWorkbenchWindowState method on IWorkbenchWindowConfigurer rather than being on IWorkbenchConfigurer and having to take an IWorkbenchWindow arg. Should also call it "saveState" for consistency with other workbench API (e.g. IPersistable/IPersistableElement). For symmetry and consistency with the rest of the advisor methods, the restore method should probably return an IWorkbenchWindowConfigurer too.
Created attachment 19240 [details] updated patch to check against head Patch updated based on Nick's comments in #34.
Created attachment 19242 [details] Updated patch to check against head, with error reporting added some error reporting
Thanks Matt, I've reviewed and released the last patch with the following tweaks: - changed restoreWorkbenchWindow to return an IWorkbenchWindowConfigurer - described default behaviour and subclassing contract for advisor save/restoreState methods - minor changes to wording in Javadoc If you could supply JUnit tests for these new APIs for M6, that would be great. Such tests are especially important in cases like this, where the IDE will not be exercising the API.
You can take a look at the existing RCP test suite in org.eclipse.ui.tests.rcp. When creating the "JUnit Plug-in Test" launch config for RCPTestSuite, specify "[No Application] - Headless mode" as the application, as these test are intended to drive the workbench as if they were the app, not run within an existing app like the other UI tests.
I read your proposal and implementations and I'am very happy concerning your current state of development - congratulations! Matthews interface nicely allows additional user-specific save/restore steps and also on-demand-restore/save, which is very fine. One weak point, as also recognized by you in point (1) of comment #26, is that this does not allow a full-fledged user-change during runtime (at least, if I read everything carefully enough), which indeed applies for our application. One example would be a realization of user-specific perspective loading as described in thread "How to dynamically chose a perspective if configurer.setSaveAndRestore(true)?". Given my original bug description I think one should accept the currently proposed AP extensions and marking this bug as fixed (because the originally demanded callback points do now exist). But I would really appreciate further developement concerning a full user-based save-restore mechanism, because I assume, that user-based RCP's play an important "role" ;-). Do you see a realistic chance for that request? I would also like to humbly ask, how you resolve this issue given the current state? Lets again take the example of user-specific perspective loading. The current behaviour always loads the perspective of the most recent saving step, but that saving step might belong to any arbitrary user. Do you see any point to realize that?
Closing PR as fixed. Filed bug 89637 for the tests. Please open separate PRs if problems with the new API are encountered. > I would really appreciate further developement concerning a full user-based save-restore mechanism ... Do you see any point to realize that? I think we should continue the discussion. Could you please open a separate enhancement request, providing details of your scenario and how you envision handling other state like preferences, workspace or other model data, etc.? > how you resolve this issue given the current state? I think the best (and safest) approach in 3.1 is to only allow user switching on startup, and choose a different instance area based on user id (similarly to how the IDE prompts for workspace on startup -- see IDEApplication in the org.eclipse.ui.ide plug-in). You could try using the APIs added here, e.g, - within Bob's session, allow a "Change User" action - this prompts for a new user - user chooses "Fred" - your app then: - uses the new APIs to save the state for all open windows in a memento - saves the memento in your plug-in's metadata area, keyed by Bob's id - closes all open windows - retrieves the memento for Fred (if any) - restores the windows previously stored there using the new APIs However, this will not save any workbench state above the level of windows, and does not address other per-user state (prefs, data model, etc).
I am in a very similar situation. We have an intro from which the user can login. After the login is when I want to restore the workbench and I havent been able to figure out a way to do this properly. I tried deffering restoring the workbench until the user logs in, and doing the following, but it opens a new window ApplicationWorkbenchAdvisor advisor = ApplicationWorkbenchAdvisor.getInstance(); IWorkbenchConfigurer config = advisor.getConfigurer(); config.setSaveAndRestore(true); config.restoreState(); Also, another thing I noticed is that if you have a view maximized before quitting the app, the intro will not be shown on re-launch of the application. This is what prompted me to try to defer the restoring the workbench.