| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html lang="en"> |
| <HEAD> |
| |
| <meta name="copyright" content="Copyright (c) IBM Corporation and others 2000, 2005. This page is made available under license. For full details see the LEGAL in the documentation book that contains this page." > |
| |
| <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1"> |
| <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css"> |
| |
| <LINK REL="STYLESHEET" HREF="../book.css" CHARSET="ISO-8859-1" TYPE="text/css"> |
| <TITLE> |
| Workspace save participation |
| </TITLE> |
| |
| <link rel="stylesheet" type="text/css" HREF="../book.css"> |
| </HEAD> |
| <BODY BGCOLOR="#ffffff"> |
| <H2> |
| Workspace save participation</H2> |
| <P > |
| Workspace save processing is triggered when the workbench is shut down by the user and at other times |
| periodically by the platform. Plug-ins can participate in the workspace save process so that critical |
| plug-in data is saved to disk whenever the rest of the workspace's persistent data is saved. </P> |
| <P > |
| The workspace save process can also be used to track changes that occur between activations of your plug-in.</P> |
| |
| <H3> |
| Implementing a save participant</H3> |
| <P > |
| To participate in workspace saving, you must add a save participant to the workspace. This is typically done during your plug-in's startup method. |
| This is also where you read any state that you might have saved |
| when your plug-in was last shut down.</P> |
| <P > |
| Let's look at a simple plug-in which will demonstrate the save process.</P> |
| <pre> |
| package com.example.saveparticipant; |
| |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.core.resources.*; |
| import java.io.File; |
| import java.util.*; |
| |
| public class MyPlugin extends Plugin { |
| private static MyPlugin plugin; |
| |
| public MyPlugin(IPluginDescriptor descriptor) { |
| super(descriptor); |
| plugin = this; |
| } |
| |
| public static MyPlugin getDefault() { |
| return plugin; |
| } |
| |
| protected void readStateFrom(File target) { |
| } |
| |
| public void startup() throws CoreException { |
| super.startup(); |
| ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant(); |
| ISavedState lastState = |
| <b>ResourcesPlugin.getWorkspace().addSaveParticipant(this, saveParticipant);</b> |
| if (lastState == null) |
| return; |
| IPath location = lastState.lookup(new Path("save")); |
| if (location == null) |
| return; |
| // the plugin instance should read any important state from the file. |
| File f = getStateLocation().append(location).toFile(); |
| readStateFrom(f); |
| } |
| |
| protected void writeImportantState(File target) { |
| } |
| }</pre> |
| <P > |
| <b><a href="../reference/api/org/eclipse/core/resources/ISaveParticipant.html"> |
| ISaveParticipant</a></b> defines the protocol for a workspace save participant. Implementors of this |
| interface can provide behavior for different stages of the save process. |
| Let's look at the stages and how our class <b>WorkspaceSaveParticipant</b> |
| implements each of these steps.</P> |
| <ul> |
| <li> |
| <b> |
| prepareToSave</b> notifies the participant that the workspace is about to be saved and that it should suspend normal operation until further notice. |
| Our save particpant does nothing here.</li> |
| </ul> |
| <pre> |
| public void prepareToSave(ISaveContext context) throws CoreException { |
| } |
| </pre> |
| <ul> |
| <li> |
| <b> |
| saving</b> tells the participant to save its important state.</li> |
| </ul> |
| <pre> |
| public void saving(ISaveContext context) throws CoreException { |
| switch (context.getKind()) { |
| case ISaveContext.FULL_SAVE: |
| MyPlugin myPluginInstance = MyPlugin.getDefault(); |
| // save the plug-in state |
| <b>int saveNumber = context.getSaveNumber(); |
| String saveFileName = "save-" + Integer.toString(saveNumber); |
| File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); |
| </b>// if we fail to write, an exception is thrown and we do not update the path |
| <b>myPluginInstance.writeImportantState(f); |
| context.map(new Path("save"), new Path(saveFileName)); |
| context.needSaveNumber();</b> |
| break; |
| case ISaveContext.PROJECT_SAVE: |
| // get the project related to this save operation |
| IProject project = context.getProject(); |
| // save its information, if necessary |
| break; |
| case ISaveContext.SNAPSHOT: |
| // This operation needs to be really fast because |
| // snapshots can be requested frequently by the |
| // workspace. |
| break; |
| } |
| } |
| </pre> |
| <blockquote> |
| <p>The <a href="../reference/api/org/eclipse/core/resources/ISaveContext.html"><b> ISaveContext</b></a> describes information about the save operation. |
| There are three kinds of save operations: <b>FULL_SAVE</b>, |
| <b>SNAPSHOT</b>, and <b>PROJECT_SAVE</b>. |
| Save participants should be careful to perform the processing appropriate for |
| the kind of save event they have received. For example, snapshot events |
| may occur quite frequently and are intended to allow plug-ins to save their |
| critical state. Taking a long time to save state which can be recomputed |
| in the event of a crash will slow down the platform.</p> |
| <P > |
| A save number is used to create data save files that are named using sequential numbers (<b>save-1</b>, |
| <b>save-2</b>, etc.) Each save file is mapped to a logical file name (<b>save</b>) that is independent of the save number. Plug-in data is written to the corresponding file and can be retrieved later without knowing the specific save number of the last successful save operation. |
| Recall that we saw this technique in our plug-in's startup code:</P> |
| <pre> |
| <b>IPath location = lastState.lookup(new Path("save"));</b> |
| </pre> |
| <P > |
| After we have saved our data and mapped the file name, we call <b>needSaveNumber</b> |
| to indicate that we have actively participated in a workspace save and want to assign a number to the save activity. |
| The save numbers can be used to create data files as above. </P> |
| |
| |
| </blockquote> |
| <ul> |
| <li> |
| <b> |
| doneSaving</b> notifies the participant that the workspace has been saved and the participant can continue normal operation.</li> |
| </ul> |
| <pre> |
| public void doneSaving(ISaveContext context) { |
| MyPlugin myPluginInstance = MyPlugin.getDefault(); |
| |
| // delete the old saved state since it is not necessary anymore |
| int previousSaveNumber = <b>context.getPreviousSaveNumber()</b>; |
| String oldFileName = "save-" + Integer.toString(previousSaveNumber); |
| File f = myPluginInstance.getStateLocation().append(oldFileName).toFile(); |
| f.delete(); |
| } |
| </pre> |
| <blockquote> |
| <p>Here, we clean up the save information from the previous save |
| operation. We use <b>getPreviousSaveNumber</b> |
| to get the save number that was assigned in the previous save operation (not |
| the one we just completed). We use this number to construct the name of |
| the file that we need to delete. Note that we do not use the save |
| state's logical file map since we've already mapped our current save file |
| number. </p> |
| </blockquote> |
| <ul> |
| <li> |
| <b> |
| rollback</b> tells the participant to rollback the important state because the save operation has failed.</li> |
| </ul> |
| <pre> |
| public void rollback(ISaveContext context) { |
| MyPlugin myPluginInstance = MyPlugin.getDefault(); |
| |
| // since the save operation has failed, delete the saved state we have just written |
| int saveNumber = context.getSaveNumber(); |
| String saveFileName = "save-" + Integer.toString(saveNumber); |
| File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); |
| f.delete(); |
| } |
| </pre> |
| <blockquote> |
| <p>Here, we delete the state that we just saved. Note that we use the |
| current save number to construct the file name of the file we just saved. |
| We don't have to worry about the fact that we mapped this file name into the <a href="../reference/api/org/eclipse/core/resources/ISaveContext.html"><b> ISaveContext</b></a><b>. |
| </b>The platform will discard the context when a save operation fails.</p> |
| </blockquote> |
| |
| |
| <p>If your plug-in throws an exception at any time during the save lifecycle, it |
| will be removed from the current save operation and will not get any of the |
| remaining lifecycle methods. For example, if you fail during your <b>saving</b> |
| method, you will not receive a |
| <b> |
| rollback</b> or |
| <b> |
| doneSaving</b> message. </p> |
| |
| |
| <H3> |
| Using previously saved state</H3> |
| <P > |
| When you add a save participant to the workspace, it will return an <b><a href="../reference/api/org/eclipse/core/resources/ISavedState.html"> ISavedState</a></b> |
| object, which describes what your plug-in saved during its last save operation (or |
| <b> null</b> if your plug-in has not previously saved any state). This |
| object can be used to access information from the previous save file (using the save number and file map) or to process changes that have occurred between activations of a plug-in.</P> |
| |
| <H4> |
| Accessing the save files</H4> |
| <P > |
| If a file map was used to save logically named files according to the save number, this same map can be used to retrieve the data from the last known save state.</P> |
| <pre> |
| ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant(); |
| ISavedState lastState = |
| ResourcesPlugin.getWorkspace().addSaveParticipant(myPluginInstance, saveParticipant); |
| |
| if (lastState != null) { |
| String saveFileName = lastState.lookup(new Path("save")).toString(); |
| File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); |
| // the plugin instance should read any important state from the file. |
| myPluginInstance.readStateFrom(f); |
| } |
| </pre> |
| |
| |
| <H4> |
| Processing resource deltas between activations</H4> |
| <P > |
| Recall that any number of resource change events could occur in the workspace before your plug-in is ever activated. If you want to know what changes have occurred since your plug-in was |
| deactivated, you can use the save mechanism to do so, even if you don't need to save any other data.</P> |
| <P > |
| The save participant must request that the platform keep a resource delta on its behalf. This is done as part of the save operation.</P> |
| <pre> |
| public void saving(ISaveContext context) throws CoreException { |
| // no state to be saved by the plug-in, but request a |
| // resource delta to be used on next activation. |
| context.needDelta(); |
| } |
| </pre> |
| <P > |
| During plug-in startup, the previous saved state can be accessed and change events will be created for all changes that have occurred since the last save.</P> |
| <pre> |
| ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant(); |
| ISavedState lastState = |
| ResourcesPlugin.getWorkspace().addSaveParticipant(myPluginInstance, saveParticipant); |
| if (lastState != null) { |
| lastState.processResourceChangeEvents(new MyResourceChangeReporter()); |
| } |
| </pre> |
| <P > |
| The provided class must implement <a href="../reference/api/org/eclipse/core/resources/IResourceChangeListener.html"><b>IResourceChangeListener</b></a>, as described in |
| <a HREF="resAdv_events.htm" CLASS="XRef"> Tracking resource changes</a>. The changes since the last save are reported as part of the <b> POST_AUTO_BUILD</b> resource change event.</P> |
| <blockquote><i> |
| Note: Marker changes are not reported in the change events stored in an <b> ISavedState</b>. |
| You must assume that any or all markers have changed since your last state was saved.</i></blockquote> |
| |
| </BODY> |
| </HTML> |