ワークスペース保管処理は、ワークベンチがユーザーによってシャットダウンされた時、およびプラットフォームによって定期的に起動されます。 プラグインは、ワークスペース保管処理に参加することができます。このため、ワークスペースの残りの永続データが保管される時はいつも、 重要なプラグイン・データはディスクに保管されます。
ワークスペース保管処理は、ユーザーのプラグインの起動と起動の間に発生する変更の追跡に使用することもできます。
ワークスペースの保管処理に参加するには、保管処理参加プログラムをワークスペースに追加する必要があります。 これは通常、プラグインの始動メソッドで行われます。 ここで、プラグインが最後にシャットダウンされたときに保管した可能性のある状態の読み取りも行われます。
保管プロセスを示す単純なプラグインを見てみましょう。
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 = ResourcesPlugin.getWorkspace().addSaveParticipant(this, saveParticipant); 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) { } }
ISaveParticipant は、 ワークスペース保管参加プログラム用のプロトコルを定義します。このインターフェースのインプリメンターは、 保管処理の異なる段階における振る舞いを提供できます。 この段階、およびユーザー・クラス WorkspaceSaveParticipant がどのようにこれらの各ステップをインプリメントするかを見てみましょう。
public void prepareToSave(ISaveContext context) throws CoreException { }
public void saving(ISaveContext context) throws CoreException { switch (context.getKind()) { case ISaveContext.FULL_SAVE: MyPlugin myPluginInstance = MyPlugin.getDefault(); // save the plug-in state int saveNumber = context.getSaveNumber(); String saveFileName = "save-" + Integer.toString(saveNumber); File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); // if we fail to write, an exception is thrown and we do not update the path myPluginInstance.writeImportantState(f); context.map(new Path("save"), new Path(saveFileName)); context.needSaveNumber(); 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; } }
ISaveContext は、保管操作に関する情報を記述します。 保管操作には次の 3 種類あります。 FULL_SAVE、SNAPSHOT、および PROJECT_SAVE です。 保管処理参加プログラムは、受信した保管イベントの種類に適切な処理を注意深く実行する必要があります。 例えば、スナップショット・イベントは非常に頻繁に発生し、 プラグインがその重要な状態を保管できるようにするためのものです。 破損の際に、再計算可能な状態の保管に時間がかかる場合には、 プラットフォームが遅くなります。
保管番号が、順次番号を使用して命名されるデータ保管ファイル (save-1、save-2 など) の作成に使用されます。 保管ファイルはそれぞれ、保管番号とは独立した論理ファイル名にマップされます (save)。 プラグイン・データは対応するファイルに書き込まれ、最後に正常終了した保管操作の特定の保管番号を知らなくても、 後で検索できます。 この技法は、ユーザーの プラグインの始動コードにも存在していたことに注意してください。
IPath location = lastState.lookup(new Path("save"));ユーザー・データを保管して、ファイル名をマップしたら、needSaveNumber を呼び出し、 ワークスペースの保管にアクティブに参加したことを示し、その保管活動に番号を割り当てます。 保管番号は、上記のようなデータ・ファイルを作成するために使用できます。
public void doneSaving(ISaveContext context) { MyPlugin myPluginInstance = MyPlugin.getDefault(); // delete the old saved state since it is not necessary anymore int previousSaveNumber = context.getPreviousSaveNumber(); String oldFileName = "save-" + Integer.toString(previousSaveNumber); File f = myPluginInstance.getStateLocation().append(oldFileName).toFile(); f.delete(); }
ここで、直前の保管操作の保管情報をクリーンアップします。 getPreviousSaveNumber を使用して、 直前の保管操作 (たった今実行した操作のことではありません) で割り当てられた保管番号を取得します。 この番号は、削除する必要があるファイルの名前の構成に使用します。 すでに現行の保管ファイル番号はマップ済みのため、 保管状態の論理ファイル・マップは使用しないことに注意してください。
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(); }
ここでは、保管したばかりの状態を削除します。 現行保管番号を使用して保管したばかりのファイルの名前を構成することに注意してください。 このファイル名を ISaveContext にマップしたことについては注意する必要はありません。 プラットフォームは保管操作が失敗した場合に、そのコンテキストを破棄します。
ユーザーのプラグインが保管のライフ・サイクル中、いつでも例外をスローする場合には、 そのプラグインは現行保管操作から除去され、残っているライフ・サイクル・メソッドのいずれも取得しません。 例えば、ユーザーの saving メソッド中に失敗すると、 rollback または doneSaving メッセージを受信しません。
保管参加プログラムをワークスペースに追加する時は、 ISavedState オブジェクトが戻ります。 このオブジェクトは、最後の保管操作でユーザー・プラグインが保管したものを記述します (または、ユーザー・プラグインが直前にどのような状態も保管していない場合には、null が戻ります)。このオブジェクトは、(保管番号およびファイル・マップを使用して) 直前の保管ファイルの情報へのアクセス、 またはプラグインの各活動化間に発生した変更の処理に使用できます。
ファイル・マップが保管番号に従って論理的に命名されたファイルの保管に使用された場合には、 この同じマップは、最後に認識されている保管状態からのデータの検索に使用できます。
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); }
いずれの番号のリソース変更イベントも、ユーザー・プラグインが活動化される前にワークスペースで発生する可能性があるということに注意してください。ユーザー・プラグインが非活動化されてから発生した変更を知るには、 他のデータを保管する必要がない場合でも、保管メカニズムを使用して、その変更を知ることができます。
保管参加プログラムは、参加プログラムに代わってプラットフォームがリソース・デルタを保持するように要求する必要があります。これは保管操作の一部として実行されます。
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(); }
プラグインの始動中は、直前に保管された状態はアクセス可能で、変更イベントは最後の保管以後に発生したすべての変更に対して作成されます。
ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant(); ISavedState lastState = ResourcesPlugin.getWorkspace().addSaveParticipant(myPluginInstance, saveParticipant); if (lastState != null) { lastState.processResourceChangeEvents(new MyResourceChangeReporter()); }
提供されたクラスは、『リソース変更のトラッキング』の説明の通り、 IResourceChangeListener を実装している必要があります。 最後の保管以後に行われた変更は、 POST_AUTO_BUILD リソース変更イベントの一部として報告されます。
注: マーカー変更は、ISavedState に保管されている変更イベントには報告されません。 ユーザーの最後の状態が保管されてから、一部またはすべてのマーカーが変更されていることを前提にしておく必要があります。