Функции обработки сохранения рабочей области вызываются в том случае, когда пользователь закрывает рабочую среду, или периодически в некоторых других случаях самой платформой. Модули могут участвовать в процессе сохранения рабочей области, это позволит сохранять критические данные модуля на диске при каждом сохранении остальных постоянных данных рабочей области.
Процесс сохранения рабочей области также используется для отслеживания изменений, которые происходят между активациями модуля.
Для участия в процессе сохранения рабочей области необходимо добавить нового агента. Обычно он создается в методе инициализации модуля, либо во время считывания данных, сохраненных в ходе последнего завершения работы модуля.
Рассмотрим процесс сохранения на примере простого модуля.
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; // экземпляр модуля определяет важное состояние из файла. 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(); // сохранение состояния модуля int saveNumber = context.getSaveNumber(); String saveFileName = "save-" + Integer.toString(saveNumber); File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); // в случае ошибки записи создается исключительная ситуация и путь не обновляется myPluginInstance.writeImportantState(f); context.map(new Path("save"), new Path(saveFileName)); context.needSaveNumber(); break; case ISaveContext.PROJECT_SAVE: // получение проекта, связанного с данной операцией сохранения IProject project = context.getProject(); // при необходимости сохранение его информации break; case ISaveContext.SNAPSHOT: // Эта операция должна быть выполнена очень быстро, поскольку // рабочая область может очень часто запрашивать // мгновенные копии. break; } }
Класс ISaveContext содержит информацию об операции сохранения. Операции сохранения бывают трех видов: 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(); // удаление ранее сохраненного состояния 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(); // поскольку операция сохранения завершилась неудачей, следует удалить сохраненное состояние int saveNumber = context.getSaveNumber(); String saveFileName = "save-" + Integer.toString(saveNumber); File f = myPluginInstance.getStateLocation().append(saveFileName).toFile(); f.delete(); }
Этот фрагмент кода удаляет сохраненное состояние. Следует отметить, что для создания имени только что сохраненного файла используется текущий номер сохранения. Не следует беспокоиться о том, что данное имя файла было преобразовано в ISaveContext. Платформа игнорирует контекст, если произошел сбой операции сохранения.
Если во время сохранения в модуле возникает исключительная ситуация, то текущая операция сохранения отменяется, а все остальные методы жизненного цикла остаются недоступны. Например, в случае сбоя метода сохранения пользователь не получит сообщение об откате или завершении сохранения.
При добавлении агента сохранения в рабочую область функция возвращает объект ISavedState, описывающий данные, которые были сохранены во время последней операции сохранения (либо возвращается пустое значение, если модуль не сохранил состояние). Этот объект используется для доступа к информации из предыдущего резервного файла (с помощью номера сохранения и преобразования файла) либо для обработки изменений, которые произошли между активациями модуля.
Если для сохранения логически названных файлов использовалось преобразование файла в соответствии с номером сохранения, это же преобразование можно использовать для восстановления данных из последнего сохраненного состояния.
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(); // экземпляр модуля определяет важное состояние из файла. myPluginInstance.readStateFrom(f); }
Вспомним, что перед первичной активацией модуля в рабочей области может произойти любое число событий изменения ресурсов. Для отслеживания изменений, которые произошли с момента деактивации модуля, может применяться механизм сохранения, даже если не требуется сохранять какие-либо другие данные.
Агент сохранения должен запросить платформу, чтобы она самостоятельно сохраняла поправку ресурсов. Это является частью операции сохранения.
public void saving(ISaveContext context) throws CoreException { // состояние для сохранения отсутствует, но есть запрос на // использование поправки ресурсов при следующей активации. 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, не описывают изменения маркеров. В коде необходимо учитывать, что с момента последнего сохранения состояния могли измениться любые маркеры.