package org.eclipse.core.internal.resources;
import java.io.*;
import java.io.File;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.zip.*;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.internal.events.*;
import org.eclipse.core.internal.localstore.*;
import org.eclipse.core.internal.utils.*;
import org.eclipse.core.internal.watson.*;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.osgi.util.NLS;
public class SaveManager implements IElementInfoFlattener, IManager, IStringPoolParticipant {
class MasterTable extends Properties {
private static final long serialVersionUID = 1L;
@Override
public synchronized Object put(Object key, Object value) {
Object prev = super.put(key, value);
if (prev != null && ROOT_SEQUENCE_NUMBER_KEY.equals(key)) {
int prevSeqNum = Integer.parseInt((String) prev);
int currSeqNum = Integer.parseInt((String) value);
if (prevSeqNum > currSeqNum) {
super.put(key, prev);
String message = "Cannot set lower sequence number for root (previous: " + prevSeqNum + ", new: " + currSeqNum + "). Ignoring the new value.";
Policy.log(new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, message, new IllegalArgumentException(message)));
}
}
return prev;
}
}
protected static final String ROOT_SEQUENCE_NUMBER_KEY = Path.ROOT + LocalMetaArea.F_TREE;
protected static final String CLEAR_DELTA_PREFIX = "clearDelta_";
protected static final String DELTA_EXPIRATION_PREFIX = "deltaExpiration_";
protected static final int DONE_SAVING = 3;
private static final long MIN_SNAPSHOT_DELAY = 1000 * 30L;
protected static final int NO_OP_THRESHOLD = 20;
protected static final int PREPARE_TO_SAVE = 1;
protected static final int ROLLBACK = 4;
protected static final String SAVE_NUMBER_PREFIX = "saveNumber_";
protected static final int SAVING = 2;
protected ElementTree lastSnap;
protected MasterTable masterTable;
private volatile boolean isSaving = false;
protected int noopCount = 0;
protected int operationCount = 0;
protected long persistMarkers = 0l;
protected long persistSyncInfo = 0l;
protected Map<String, SavedState> savedStates;
protected Map<String, ISaveParticipant> saveParticipants;
protected final DelayedSnapshotJob snapshotJob;
protected volatile boolean snapshotRequested;
private IStatus snapshotRequestor;
protected Workspace workspace;
private static final String DEBUG_START = " starting...";
private static final String DEBUG_FULL_SAVE = "Full save on workspace: ";
private static final String DEBUG_PROJECT_SAVE = "Save on project ";
private static final String DEBUG_SNAPSHOT = "Snapshot: ";
private static final int TREE_BUFFER_SIZE = 1024 * 64;
public SaveManager(Workspace workspace) {
this.workspace = workspace;
this.masterTable = new MasterTable();
this.snapshotJob = new DelayedSnapshotJob(this);
snapshotRequested = false;
snapshotRequestor = null;
saveParticipants = Collections.synchronizedMap(new HashMap<>(10));
}
public ISavedState addParticipant(String pluginId, ISaveParticipant participant) throws CoreException {
if (saveParticipants.put(pluginId, participant) != null)
return null;
SavedState state = savedStates.get(pluginId);
if (state != null) {
if (isDeltaCleared(pluginId)) {
state.forgetTrees();
removeClearDeltaMarks(pluginId);
} else {
try {
workspace.prepareOperation(null, null);
workspace.beginOperation(true);
state.newTree = workspace.getElementTree();
} finally {
workspace.endOperation(null, false);
}
return state;
}
}
if (getSaveNumber(pluginId) > 0)
return new SavedState(workspace, pluginId, null, null);
return null;
}
protected void broadcastLifecycle(final int lifecycle, Map<String, SaveContext> contexts, final MultiStatus warnings, IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor, contexts.size());
try {
for (final Iterator<Map.Entry<String, SaveContext>> it = contexts.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, SaveContext> entry = it.next();
String pluginId = entry.getKey();
final ISaveParticipant participant = saveParticipants.get(pluginId);
if (participant == null) {
subMonitor.worked(1);
continue;
}
final SaveContext context = entry.getValue();
ISafeRunnable code = new ISafeRunnable() {
@Override
public void handleException(Throwable e) {
String message = Messages.resources_saveProblem;
IStatus status = new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES,
IResourceStatus.INTERNAL_ERROR, message, e);
warnings.add(status);
it.remove();
}
@Override
public void run() throws Exception {
executeLifecycle(lifecycle, participant, context);
}
};
SafeRunner.run(code);
subMonitor.worked(1);
}
} finally {
subMonitor.done();
}
}
protected void clearDeltaExpiration(String pluginId) {
masterTable.remove(DELTA_EXPIRATION_PREFIX + pluginId);
}
protected void cleanMasterTable() {
for (Iterator<Object> it = masterTable.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
if (!key.endsWith(LocalMetaArea.F_TREE))
continue;
String prefix = key.substring(0, key.length() - LocalMetaArea.F_TREE.length());
if (prefix.equals(Path.ROOT.toString()))
continue;
IProject project = workspace.getRoot().getProject(prefix);
if (!project.exists() || project.isOpen())
it.remove();
}
IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
IPath backup = workspace.getMetaArea().getBackupLocationFor(location);
try {
saveMasterTable(ISaveContext.FULL_SAVE, backup);
} catch (CoreException e) {
Policy.log(e.getStatus());
backup.toFile().delete();
return;
}
if (location.toFile().exists() && !location.toFile().delete())
return;
try {
saveMasterTable(ISaveContext.FULL_SAVE, location);
} catch (CoreException e) {
Policy.log(e.getStatus());
location.toFile().delete();
return;
}
backup.toFile().delete();
}
protected void clearSavedDelta() {
synchronized (saveParticipants) {
for (String pluginId : saveParticipants.keySet()) {
masterTable.setProperty(CLEAR_DELTA_PREFIX + pluginId, "true");
}
}
}
protected void collapseTrees(Map<String, SaveContext> contexts) throws CoreException {
synchronized (savedStates) {
for (SaveContext context : contexts.values()) {
forgetSavedTree(context.getPluginId());
}
}
ArrayList<ElementTree> trees = new ArrayList<>();
synchronized (savedStates) {
for (SavedState state : savedStates.values()) {
if (state.oldTree != null) {
trees.add(state.oldTree);
}
}
}
IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject project : projects) {
if (project.isOpen()) {
ArrayList<BuilderPersistentInfo> builderInfos = workspace.getBuildManager().createBuildersPersistentInfo(project);
if (builderInfos != null) {
for (BuilderPersistentInfo info : builderInfos) {
trees.add(info.getLastBuiltTree());
}
}
}
}
if (trees.isEmpty())
return;
trees.add(workspace.getElementTree());
ElementTree[] treeArray = new ElementTree[trees.size()];
trees.toArray(treeArray);
ElementTree[] sorted = sortTrees(treeArray);
if (sorted == null)
return;
for (int i = 1; i < sorted.length; i++)
sorted[i].collapseTo(sorted[i - 1]);
}
protected void commit(Map<String, SaveContext> contexts) throws CoreException {
for (SaveContext saveContext : contexts.values())
saveContext.commit();
}
protected Map<String, SaveContext> computeSaveContexts(String[] pluginIds, int kind, IProject project) {
HashMap<String, SaveContext> result = new HashMap<>(pluginIds.length);
for (String pluginId : pluginIds) {
try {
SaveContext context = new SaveContext(pluginId, kind, project);
result.put(pluginId, context);
} catch (CoreException e) {
Policy.log(e.getStatus());
}
}
return result;
}
protected Map<String, ElementTree> computeStatesToSave(Map<String, SaveContext> contexts, ElementTree current) {
HashMap<String, ElementTree> result = new HashMap<>(savedStates.size() * 2);
synchronized (savedStates) {
for (SavedState state : savedStates.values()) {
if (state.oldTree != null)
result.put(state.pluginId, state.oldTree);
}
}
for (SaveContext context : contexts.values()) {
if (!context.isDeltaNeeded())
continue;
String pluginId = context.getPluginId();
result.put(pluginId, current);
}
return result;
}
protected void executeLifecycle(int lifecycle, ISaveParticipant participant, SaveContext context) throws CoreException {
switch (lifecycle) {
case PREPARE_TO_SAVE :
participant.prepareToSave(context);
break;
case SAVING :
try {
if (ResourceStats.TRACE_SAVE_PARTICIPANTS)
ResourceStats.startSave(participant);
participant.saving(context);
} finally {
if (ResourceStats.TRACE_SAVE_PARTICIPANTS)
ResourceStats.endSave();
}
break;
case DONE_SAVING :
participant.doneSaving(context);
break;
case ROLLBACK :
participant.rollback(context);
break;
default :
Assert.isTrue(false, "Invalid save lifecycle code");
}
}
public void forgetSavedTree(String pluginId) {
if (pluginId == null) {
synchronized (savedStates) {
for (SavedState savedState : savedStates.values())
savedState.forgetTrees();
}
} else {
SavedState state = savedStates.get(pluginId);
if (state != null)
state.forgetTrees();
}
}
protected long getDeltaExpiration(String pluginId) {
String result = masterTable.getProperty(DELTA_EXPIRATION_PREFIX + pluginId);
return (result == null) ? System.currentTimeMillis() : Long.parseLong(result);
}
protected Properties getMasterTable() {
return masterTable;
}
public int getSaveNumber(String pluginId) {
String value = masterTable.getProperty(SAVE_NUMBER_PREFIX + pluginId);
return (value == null) ? 0 : Integer.parseInt(value);
}
protected String[] getSaveParticipantPluginIds() {
synchronized (saveParticipants) {
return saveParticipants.keySet().toArray(new String[saveParticipants.size()]);
}
}
private void hookEndSave(int kind, IProject project, long start) {
if (ResourceStats.TRACE_SNAPSHOT && kind == ISaveContext.SNAPSHOT)
ResourceStats.endSnapshot();
if (Policy.DEBUG_SAVE) {
String endMessage = null;
switch (kind) {
case ISaveContext.FULL_SAVE :
endMessage = DEBUG_FULL_SAVE;
break;
case ISaveContext.SNAPSHOT :
endMessage = DEBUG_SNAPSHOT;
break;
case ISaveContext.PROJECT_SAVE :
endMessage = DEBUG_PROJECT_SAVE + project.getFullPath() + ": ";
break;
}
if (endMessage != null)
Policy.debug(endMessage + (System.currentTimeMillis() - start) + "ms");
}
}
private void hookStartSave(int kind, Project project) {
if (ResourceStats.TRACE_SNAPSHOT && kind == ISaveContext.SNAPSHOT)
ResourceStats.startSnapshot();
if (Policy.DEBUG_SAVE) {
switch (kind) {
case ISaveContext.FULL_SAVE :
Policy.debug(DEBUG_FULL_SAVE + DEBUG_START);
break;
case ISaveContext.SNAPSHOT :
Policy.debug(DEBUG_SNAPSHOT + DEBUG_START);
break;
case ISaveContext.PROJECT_SAVE :
Policy.debug(DEBUG_PROJECT_SAVE + project.getFullPath() + DEBUG_START);
break;
}
}
}
protected void initSnap(IProgressMonitor monitor) {
snapshotJob.cancel();
lastSnap = workspace.getElementTree();
lastSnap.immutable();
workspace.newWorkingTree();
operationCount = 0;
IPath location = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
java.io.File target = location.toFile().getParentFile();
FilenameFilter filter = (dir, name) -> {
if (!name.endsWith(LocalMetaArea.F_SNAP))
return false;
for (int i = 0; i < name.length() - LocalMetaArea.F_SNAP.length(); i++) {
char c = name.charAt(i);
if (c < '0' || c > '9')
return false;
}
return true;
};
String[] candidates = target.list(filter);
if (candidates != null)
removeFiles(target, candidates, Collections.<String> emptyList());
}
protected boolean isDeltaCleared(String pluginId) {
String clearDelta = masterTable.getProperty(CLEAR_DELTA_PREFIX + pluginId);
return clearDelta != null && clearDelta.equals("true");
}
protected boolean isOldPluginTree(String pluginId) {
if (isDeltaCleared(pluginId))
return false;
if (Platform.getBundle(pluginId) == null)
return true;
long deltaAge = System.currentTimeMillis() - getDeltaExpiration(pluginId);
return deltaAge > workspace.internalGetDescription().getDeltaExpiration();
}
@Override
public Object readElement(IPath path, DataInput input) throws IOException {
Assert.isNotNull(path);
Assert.isNotNull(input);
int flags = input.readInt();
int type = (flags & ICoreConstants.M_TYPE) >> ICoreConstants.M_TYPE_START;
ResourceInfo info = workspace.newElement(type);
info.readFrom(flags, input);
return info;
}
private void rememberSnapshotRequestor() {
if (Policy.DEBUG_SAVE)
Policy.debug(new RuntimeException("Scheduling workspace snapshot"));
if (snapshotRequestor == null) {
String msg = "The workspace will exit with unsaved changes in this session.";
snapshotRequestor = new ResourceStatus(ICoreConstants.CRASH_DETECTED, msg);
}
}
protected void removeClearDeltaMarks() {
synchronized (saveParticipants) {
for (String pluginId : saveParticipants.keySet()) {
removeClearDeltaMarks(pluginId);
}
}
}
protected void removeClearDeltaMarks(String pluginId) {
masterTable.setProperty(CLEAR_DELTA_PREFIX + pluginId, "false");
}
protected void removeFiles(java.io.File root, String[] candidates, List<String> exclude) {
for (String candidate : candidates) {
boolean delete = true;
for (ListIterator<String> it = exclude.listIterator(); it.hasNext();) {
String s = it.next();
if (s.equals(candidate)) {
it.remove();
delete = false;
break;
}
}
if (delete)
new java.io.File(root, candidate).delete();
}
}
private void removeGarbage(DataOutputStream output, IPath location, IPath tempLocation) throws IOException {
if (output.size() == 0) {
output.close();
location.toFile().delete();
tempLocation.toFile().delete();
}
}
public void removeParticipant(String pluginId) {
saveParticipants.remove(pluginId);
}
protected void removeUnusedSafeTables() {
List<String> valuables = new ArrayList<>(10);
IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
valuables.add(location.lastSegment());
for (Enumeration<Object> e = masterTable.keys(); e.hasMoreElements();) {
String key = (String) e.nextElement();
if (key.startsWith(SAVE_NUMBER_PREFIX)) {
String pluginId = key.substring(SAVE_NUMBER_PREFIX.length());
valuables.add(workspace.getMetaArea().getSafeTableLocationFor(pluginId).lastSegment());
}
}
java.io.File target = location.toFile().getParentFile();
String[] candidates = target.list();
if (candidates == null)
return;
removeFiles(target, candidates, valuables);
}
protected void removeUnusedTreeFiles() {
List<String> valuables = new ArrayList<>(10);
IPath location = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), false);
valuables.add(location.lastSegment());
java.io.File target = location.toFile().getParentFile();
FilenameFilter filter = (dir, name) -> name.endsWith(LocalMetaArea.F_TREE);
String[] candidates = target.list(filter);
if (candidates != null)
removeFiles(target, candidates, valuables);
IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject project : projects) {
location = workspace.getMetaArea().getTreeLocationFor(project, false);
valuables.add(location.lastSegment());
target = location.toFile().getParentFile();
candidates = target.list(filter);
if (candidates != null)
removeFiles(target, candidates, valuables);
}
}
protected void reportSnapshotRequestor() {
if (snapshotRequestor != null)
Policy.log(snapshotRequestor);
}
public void requestSnapshot() {
snapshotRequested = true;
}
protected void resetSnapshots(IResource resource) throws CoreException {
Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
String message;
java.io.File file = workspace.getMetaArea().getMarkersSnapshotLocationFor(resource).toFile();
if (file.exists())
file.delete();
if (file.exists()) {
message = Messages.resources_resetMarkers;
throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, resource.getFullPath(), message, null);
}
file = workspace.getMetaArea().getSyncInfoSnapshotLocationFor(resource).toFile();
if (file.exists())
file.delete();
if (file.exists()) {
message = Messages.resources_resetSync;
throw new ResourceException(IResourceStatus.FAILED_DELETE_METADATA, resource.getFullPath(), message, null);
}
if (resource.getType() == IResource.PROJECT)
return;
IProject[] projects = ((IWorkspaceRoot) resource).getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject project : projects)
resetSnapshots(project);
}
protected void restore(IProgressMonitor monitor) throws CoreException {
if (Policy.DEBUG_RESTORE)
Policy.debug("Restore workspace: starting...");
long start = System.currentTimeMillis();
monitor = Policy.monitorFor(monitor);
try {
monitor.beginTask("", 50);
workspace.newWorkingTree();
try {
String msg = Messages.resources_startupProblems;
MultiStatus problems = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IResourceStatus.FAILED_READ_METADATA, msg, null);
restoreMasterTable();
restoreTree(Policy.subMonitorFor(monitor, 10));
restoreSnapshots(Policy.subMonitorFor(monitor, 10));
try {
restoreMarkers(workspace.getRoot(), false, Policy.subMonitorFor(monitor, 10));
} catch (CoreException e) {
problems.merge(e.getStatus());
}
try {
restoreSyncInfo(workspace.getRoot(), Policy.subMonitorFor(monitor, 10));
} catch (CoreException e) {
problems.merge(e.getStatus());
}
restoreMetaInfo(problems, Policy.subMonitorFor(monitor, 10));
IProject[] roots = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject root : roots)
((Project) root).startup();
if (!problems.isOK())
Policy.log(problems);
} finally {
workspace.getElementTree().immutable();
}
} finally {
monitor.done();
}
if (Policy.DEBUG_RESTORE)
Policy.debug("Restore workspace: " + (System.currentTimeMillis() - start) + "ms");
}
protected boolean restore(Project project, IProgressMonitor monitor) throws CoreException {
boolean status = true;
if (Policy.DEBUG_RESTORE)
Policy.debug("Restore project " + project.getFullPath() + ": starting...");
long start = System.currentTimeMillis();
monitor = Policy.monitorFor(monitor);
try {
monitor.beginTask("", 40);
if (project.isOpen()) {
status = restoreTree(project, Policy.subMonitorFor(monitor, 10));
} else {
monitor.worked(10);
}
restoreMarkers(project, true, Policy.subMonitorFor(monitor, 10));
restoreSyncInfo(project, Policy.subMonitorFor(monitor, 10));
restoreMetaInfo(project, Policy.subMonitorFor(monitor, 10));
} finally {
monitor.done();
}
if (Policy.DEBUG_RESTORE)
Policy.debug("Restore project " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
return status;
}
protected boolean restoreFromRefreshSnapshot(Project project, IProgressMonitor monitor) throws CoreException {
boolean status = true;
IPath snapshotPath = workspace.getMetaArea().getRefreshLocationFor(project);
java.io.File snapshotFile = snapshotPath.toFile();
if (!snapshotFile.exists())
return false;
if (Policy.DEBUG_RESTORE)
Policy.debug("Restore project " + project.getFullPath() + ": starting...");
long start = System.currentTimeMillis();
monitor = Policy.monitorFor(monitor);
try {
monitor.beginTask("", 40);
status = restoreTreeFromRefreshSnapshot(project, snapshotFile, Policy.subMonitorFor(monitor, 40));
if (status) {
ProjectDescription description = workspace.getFileSystemManager().read(project, true);
project.internalSetDescription(description, false);
workspace.getMetaArea().clearRefresh(project);
}
} finally {
monitor.done();
}
if (Policy.DEBUG_RESTORE)
Policy.debug("Restore project " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
return status;
}
protected void (IResource resource, boolean generateDeltas, IProgressMonitor monitor) throws CoreException {
Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
long start = System.currentTimeMillis();
MarkerManager markerManager = workspace.getMarkerManager();
if (resource.isAccessible())
markerManager.restore(resource, generateDeltas, monitor);
if (resource.getType() == IResource.PROJECT) {
if (Policy.DEBUG_RESTORE_MARKERS) {
Policy.debug("Restore Markers for " + resource.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
}
return;
}
IProject[] projects = ((IWorkspaceRoot) resource).getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject project : projects)
if (project.isAccessible())
markerManager.restore(project, generateDeltas, monitor);
if (Policy.DEBUG_RESTORE_MARKERS) {
Policy.debug("Restore Markers for workspace: " + (System.currentTimeMillis() - start) + "ms");
}
}
protected void restoreMasterTable() throws CoreException {
long start = System.currentTimeMillis();
masterTable.clear();
IPath location = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES);
java.io.File target = location.toFile();
if (!target.exists()) {
location = workspace.getMetaArea().getBackupLocationFor(location);
target = location.toFile();
if (!target.exists())
return;
}
try (SafeChunkyInputStream input = new SafeChunkyInputStream(target)) {
masterTable.load(input);
} catch (IOException e) {
String message = Messages.resources_exMasterTable;
throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, message, e);
}
if (Policy.DEBUG_RESTORE_MASTERTABLE)
Policy.debug("Restore master table for " + location + ": " + (System.currentTimeMillis() - start) + "ms");
}
protected void restoreMetaInfo(MultiStatus problems, IProgressMonitor monitor) {
if (Policy.DEBUG_RESTORE_METAINFO)
Policy.debug("Restore workspace metainfo: starting...");
long start = System.currentTimeMillis();
IProject[] roots = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject root : roots) {
try {
restoreMetaInfo((Project) root, monitor);
} catch (CoreException e) {
String message = NLS.bind(Messages.resources_readMeta, root.getName());
problems.merge(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, root.getFullPath(), message, e));
}
}
if (Policy.DEBUG_RESTORE_METAINFO)
Policy.debug("Restore workspace metainfo: " + (System.currentTimeMillis() - start) + "ms");
}
protected void restoreMetaInfo(Project project, IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
ProjectDescription description = null;
CoreException failure = null;
try {
if (project.isOpen())
description = workspace.getFileSystemManager().read(project, true);
else
description = workspace.getMetaArea().readOldDescription(project);
} catch (CoreException e) {
failure = e;
}
if (description == null) {
description = new ProjectDescription();
description.setName(project.getName());
workspace.getMetaArea().readPrivateDescription(project, description);
}
project.internalSetDescription(description, false);
if (failure != null) {
writeTree(project, IResource.DEPTH_INFINITE);
project.internalClose(monitor);
throw failure;
}
if (Policy.DEBUG_RESTORE_METAINFO)
Policy.debug("Restore metainfo for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
}
protected void restoreSnapshots(IProgressMonitor monitor) {
long start = System.currentTimeMillis();
monitor = Policy.monitorFor(monitor);
String message;
try {
monitor.beginTask("", Policy.totalWork);
IPath snapLocation = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
java.io.File localFile = snapLocation.toFile();
if (!localFile.exists()) {
snapLocation = workspace.getMetaArea().getLegacySnapshotLocationFor(workspace.getRoot());
localFile = snapLocation.toFile();
if (!localFile.exists() || isSnapshotOlderThanTree(localFile)) {
initSnap(Policy.subMonitorFor(monitor, Policy.totalWork / 2));
return;
}
}
workspace.setCrashed(true);
try {
ElementTree complete = workspace.getElementTree();
complete.immutable();
try (
DataInputStream input = new DataInputStream(new SafeChunkyInputStream(localFile));
) {
WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt());
complete = reader.readSnapshotTree(input, complete, monitor);
} finally {
lastSnap = complete;
complete = complete.newEmptyDelta();
workspace.tree = complete;
}
} catch (Exception e) {
message = Messages.resources_snapRead;
Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_METADATA, null, message, e));
}
} finally {
monitor.done();
}
if (Policy.DEBUG_RESTORE_SNAPSHOTS)
Policy.debug("Restore snapshots for workspace: " + (System.currentTimeMillis() - start) + "ms");
}
private boolean isSnapshotOlderThanTree(File snapshot) {
IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), false);
File tree = treeLocation.toFile();
if (!tree.exists()) {
treeLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
tree = treeLocation.toFile();
if (!tree.exists())
return false;
}
return snapshot.lastModified() < tree.lastModified();
}
protected void restoreSyncInfo(IResource resource, IProgressMonitor monitor) throws CoreException {
Assert.isLegal(resource.getType() == IResource.ROOT || resource.getType() == IResource.PROJECT);
long start = System.currentTimeMillis();
Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
if (resource.isAccessible())
synchronizer.restore(resource, monitor);
if (resource.getType() == IResource.PROJECT) {
if (Policy.DEBUG_RESTORE_SYNCINFO) {
Policy.debug("Restore SyncInfo for " + resource.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
}
return;
}
IProject[] projects = ((IWorkspaceRoot) resource).getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject project : projects)
if (project.isAccessible())
synchronizer.restore(project, monitor);
if (Policy.DEBUG_RESTORE_SYNCINFO) {
Policy.debug("Restore SyncInfo for workspace: " + (System.currentTimeMillis() - start) + "ms");
}
}
protected void restoreTree(IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), false);
IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
if (!treeLocation.toFile().exists() && !tempLocation.toFile().exists()) {
savedStates = Collections.synchronizedMap(new HashMap<>(10));
return;
}
try (DataInputStream input = new DataInputStream(new SafeFileInputStream(treeLocation.toOSString(), tempLocation.toOSString(), TREE_BUFFER_SIZE))) {
WorkspaceTreeReader.getReader(workspace, input.readInt()).readTree(input, monitor);
} catch (IOException e) {
String msg = NLS.bind(Messages.resources_readMeta, treeLocation.toOSString());
throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, treeLocation, msg, e);
}
if (Policy.DEBUG_RESTORE_TREE) {
Policy.debug("Restore Tree for workspace: " + (System.currentTimeMillis() - start) + "ms");
}
}
protected boolean restoreTree(Project project, IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
monitor = Policy.monitorFor(monitor);
String message;
try {
monitor.beginTask("", Policy.totalWork);
IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(project, false);
IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
if (!treeLocation.toFile().exists() && !tempLocation.toFile().exists())
return false;
try (
DataInputStream input = new DataInputStream(new SafeFileInputStream(treeLocation.toOSString(), tempLocation.toOSString()));
) {
WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt());
reader.readTree(project, input, Policy.subMonitorFor(monitor, Policy.totalWork));
}
} catch (IOException e) {
message = NLS.bind(Messages.resources_readMeta, project.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, project.getFullPath(), message, e);
} finally {
monitor.done();
}
if (Policy.DEBUG_RESTORE_TREE) {
Policy.debug("Restore Tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
}
return true;
}
protected boolean restoreTreeFromRefreshSnapshot(Project project, java.io.File snapshotFile, IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
monitor = Policy.monitorFor(monitor);
String message;
IPath snapshotPath = null;
try {
monitor.beginTask("", Policy.totalWork);
InputStream snapIn = new FileInputStream(snapshotFile);
ZipInputStream zip = new ZipInputStream(snapIn);
ZipEntry treeEntry = zip.getNextEntry();
if (treeEntry == null || !treeEntry.getName().equals("resource-index.tree")) {
zip.close();
return false;
}
try (
DataInputStream input = new DataInputStream(zip);
) {
WorkspaceTreeReader reader = WorkspaceTreeReader.getReader(workspace, input.readInt(), true);
reader.readTree(project, input, Policy.subMonitorFor(monitor, Policy.totalWork));
} finally {
zip.close();
}
} catch (IOException e) {
snapshotPath = new Path(snapshotFile.getPath());
message = NLS.bind(Messages.resources_readMeta, snapshotPath);
throw new ResourceException(IResourceStatus.FAILED_READ_METADATA, snapshotPath, message, e);
} finally {
monitor.done();
}
if (Policy.DEBUG_RESTORE_TREE) {
Policy.debug("Restore Tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
}
return true;
}
class InternalMonitorWrapper extends ProgressMonitorWrapper {
private boolean ignoreCancel;
public InternalMonitorWrapper(IProgressMonitor monitor) {
super(SubMonitor.convert(monitor));
}
public void ignoreCancelState(boolean ignore) {
this.ignoreCancel = ignore;
}
@Override
public boolean isCanceled() {
return ignoreCancel ? false : super.isCanceled();
}
}
public IStatus save(int kind, Project project, IProgressMonitor monitor) throws CoreException {
return save(kind, false, project, monitor);
}
public IStatus save(int kind, boolean keepConsistencyWhenCanceled, Project project, IProgressMonitor parentMonitor) throws CoreException {
InternalMonitorWrapper monitor = new InternalMonitorWrapper(parentMonitor);
monitor.ignoreCancelState(keepConsistencyWhenCanceled);
try {
isSaving = true;
String message = Messages.resources_saving_0;
monitor.beginTask(message, 7);
message = Messages.resources_saveWarnings;
MultiStatus warnings = new MultiStatus(ResourcesPlugin.PI_RESOURCES, IStatus.WARNING, message, null);
ISchedulingRule rule = project != null ? (IResource) project : workspace.getRoot();
try {
workspace.prepareOperation(rule, monitor);
workspace.beginOperation(false);
hookStartSave(kind, project);
long start = System.currentTimeMillis();
Map<String, SaveContext> contexts = computeSaveContexts(getSaveParticipantPluginIds(), kind, project);
broadcastLifecycle(PREPARE_TO_SAVE, contexts, warnings, Policy.subMonitorFor(monitor, 1));
try {
broadcastLifecycle(SAVING, contexts, warnings, Policy.subMonitorFor(monitor, 1));
switch (kind) {
case ISaveContext.FULL_SAVE :
saveTree(contexts, Policy.subMonitorFor(monitor, 1));
initSnap(null);
snapshotRequestor = null;
cleanMasterTable();
persistMarkers = 0l;
persistSyncInfo = 0l;
visitAndSave(workspace.getRoot());
monitor.worked(1);
if (Policy.DEBUG_SAVE) {
Policy.debug("Total Save Markers: " + persistMarkers + "ms");
Policy.debug("Total Save Sync Info: " + persistSyncInfo + "ms");
}
resetSnapshots(workspace.getRoot());
removeUnusedSafeTables();
removeUnusedTreeFiles();
monitor.ignoreCancelState(false);
workspace.getFileSystemManager().getHistoryStore().clean(Policy.subMonitorFor(monitor, 1));
monitor.ignoreCancelState(keepConsistencyWhenCanceled);
saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
break;
case ISaveContext.SNAPSHOT :
snapTree(workspace.getElementTree(), Policy.subMonitorFor(monitor, 1));
persistMarkers = 0l;
persistSyncInfo = 0l;
visitAndSnap(workspace.getRoot());
monitor.worked(1);
if (Policy.DEBUG_SAVE) {
Policy.debug("Total Snap Markers: " + persistMarkers + "ms");
Policy.debug("Total Snap Sync Info: " + persistSyncInfo + "ms");
}
collapseTrees(contexts);
clearSavedDelta();
saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
break;
case ISaveContext.PROJECT_SAVE :
writeTree(project, IResource.DEPTH_INFINITE);
monitor.worked(1);
visitAndSave(project);
monitor.worked(1);
resetSnapshots(project);
IStatus result = saveMetaInfo(project, null);
if (!result.isOK())
warnings.merge(result);
monitor.worked(1);
break;
}
commit(contexts);
if (kind == ISaveContext.FULL_SAVE)
removeClearDeltaMarks();
saveMasterTable(kind);
broadcastLifecycle(DONE_SAVING, contexts, warnings, Policy.subMonitorFor(monitor, 1));
hookEndSave(kind, project, start);
return warnings;
} catch (CoreException e) {
broadcastLifecycle(ROLLBACK, contexts, warnings, Policy.subMonitorFor(monitor, 1));
restoreMasterTable();
throw e;
}
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
} finally {
workspace.endOperation(rule, false);
}
} finally {
isSaving = false;
monitor.done();
}
}
protected void saveMasterTable(int kind) throws CoreException {
saveMasterTable(kind, workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES));
}
protected void saveMasterTable(int kind, IPath location) throws CoreException {
long start = System.currentTimeMillis();
java.io.File target = location.toFile();
try {
if (kind == ISaveContext.FULL_SAVE || kind == ISaveContext.SNAPSHOT)
validateMasterTableBeforeSave(target);
try (
SafeChunkyOutputStream output = new SafeChunkyOutputStream(target);
) {
masterTable.store(output, "master table");
output.succeed();
}
} catch (IOException e) {
throw new ResourceException(IResourceStatus.INTERNAL_ERROR, null, NLS.bind(Messages.resources_exSaveMaster, location.toOSString()), e);
}
if (Policy.DEBUG_SAVE_MASTERTABLE)
Policy.debug("Save master table for " + location + ": " + (System.currentTimeMillis() - start) + "ms");
}
protected void saveMetaInfo(MultiStatus problems, IProgressMonitor monitor) throws CoreException {
if (Policy.DEBUG_SAVE_METAINFO)
Policy.debug("Save workspace metainfo: starting...");
long start = System.currentTimeMillis();
ResourcesPlugin.getPlugin().savePluginPreferences();
IProject[] roots = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject root : roots)
if (root.isAccessible()) {
IStatus result = saveMetaInfo((Project) root, null);
if (!result.isOK())
problems.merge(result);
}
if (Policy.DEBUG_SAVE_METAINFO)
Policy.debug("Save workspace metainfo: " + (System.currentTimeMillis() - start) + "ms");
}
protected IStatus saveMetaInfo(Project project, IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
if (!workspace.getFileSystemManager().hasSavedDescription(project)) {
workspace.getFileSystemManager().writeSilently(project);
String msg = NLS.bind(Messages.resources_missingProjectMetaRepaired, project.getName());
return new ResourceStatus(IResourceStatus.MISSING_DESCRIPTION_REPAIRED, project.getFullPath(), msg);
}
if (Policy.DEBUG_SAVE_METAINFO)
Policy.debug("Save metainfo for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
return Status.OK_STATUS;
}
public void saveRefreshSnapshot(Project project, URI snapshotLocation, IProgressMonitor monitor) throws CoreException {
IFileStore store = EFS.getStore(snapshotLocation);
IPath snapshotPath = new Path(snapshotLocation.getPath());
java.io.File tmpTree = null;
try {
tmpTree = java.io.File.createTempFile("tmp", ".tree");
} catch (IOException e) {
throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, snapshotPath, Messages.resources_copyProblem, e);
}
ZipOutputStream out = null;
try {
FileOutputStream fis = new FileOutputStream(tmpTree);
try (
DataOutputStream output = new DataOutputStream(fis);
) {
output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
writeTree(project, output, monitor);
}
OutputStream snapOut = store.openOutputStream(EFS.NONE, monitor);
out = new ZipOutputStream(snapOut);
out.setLevel(Deflater.BEST_COMPRESSION);
ZipEntry e = new ZipEntry("resource-index.tree");
out.putNextEntry(e);
int read = 0;
byte[] buffer = new byte[4096];
try (
InputStream in = new FileInputStream(tmpTree);
) {
while ((read = in.read(buffer)) >= 0) {
out.write(buffer, 0, read);
}
out.closeEntry();
}
out.close();
} catch (IOException e) {
throw new ResourceException(IResourceStatus.FAILED_WRITE_LOCAL, snapshotPath, Messages.resources_copyProblem, e);
} finally {
FileUtil.safeClose(out);
if (tmpTree != null)
tmpTree.delete();
}
}
protected void saveTree(Map<String, SaveContext> contexts, IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(workspace.getRoot(), true);
try {
IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
try (
DataOutputStream output = new DataOutputStream(new SafeFileOutputStream(treeLocation.toOSString(), tempLocation.toOSString()));
) {
output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
writeTree(computeStatesToSave(contexts, workspace.getElementTree()), output, monitor);
}
} catch (Exception e) {
String msg = NLS.bind(Messages.resources_writeWorkspaceMeta, treeLocation);
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, Path.ROOT, msg, e);
}
if (Policy.DEBUG_SAVE_TREE)
Policy.debug("Save Workspace Tree: " + (System.currentTimeMillis() - start) + "ms");
}
void setPluginsSavedState(HashMap<String, SavedState> savedStates) {
this.savedStates = Collections.synchronizedMap(savedStates);
}
protected void setSaveNumber(String pluginId, int number) {
masterTable.setProperty(SAVE_NUMBER_PREFIX + pluginId, Integer.toString(number));
}
@Override
public void shareStrings(StringPool pool) {
lastSnap.shareStrings(pool);
}
@Override
public void shutdown(final IProgressMonitor monitor) {
int state = snapshotJob.getState();
if (state == Job.WAITING || state == Job.SLEEPING)
snapshotJob.run(SubMonitor.convert(monitor));
snapshotJob.cancel();
}
public void snapshotIfNeeded(boolean hasTreeChanges) {
if (isSaving)
return;
if (snapshotRequested || operationCount >= workspace.internalGetDescription().getOperationsPerSnapshot()) {
rememberSnapshotRequestor();
if (snapshotJob.getState() == Job.NONE)
snapshotJob.schedule();
else
snapshotJob.wakeUp();
} else {
if (hasTreeChanges) {
operationCount++;
if (snapshotJob.getState() == Job.NONE) {
rememberSnapshotRequestor();
long interval = workspace.internalGetDescription().getSnapshotInterval();
snapshotJob.schedule(Math.max(interval, MIN_SNAPSHOT_DELAY));
}
} else {
if (++noopCount > NO_OP_THRESHOLD) {
operationCount++;
noopCount = 0;
}
}
}
}
protected void snapTree(ElementTree tree, IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
String message;
SubMonitor subMonitor = SubMonitor.convert(monitor, Policy.totalWork);
try {
tree.immutable();
if (tree == lastSnap)
return;
operationCount = 0;
IPath snapPath = workspace.getMetaArea().getSnapshotLocationFor(workspace.getRoot());
ElementTreeWriter writer = new ElementTreeWriter(this);
java.io.File localFile = snapPath.toFile();
try {
SafeChunkyOutputStream safeStream = new SafeChunkyOutputStream(localFile);
try (DataOutputStream out = new DataOutputStream(safeStream);) {
out.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
writeWorkspaceFields(out, subMonitor);
writer.writeDelta(tree, lastSnap, Path.ROOT, ElementTreeWriter.D_INFINITE, out,
ResourceComparator.getSaveComparator());
safeStream.succeed();
out.close();
}
} catch (IOException e) {
message = NLS.bind(Messages.resources_writeWorkspaceMeta, localFile.getAbsolutePath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, Path.ROOT, message, e);
}
lastSnap = tree;
if (Policy.DEBUG_SAVE_TREE)
Policy.debug("Snapshot Workspace Tree: " + (System.currentTimeMillis() - start) + "ms");
} finally {
subMonitor.done();
}
}
protected ElementTree[] sortTrees(ElementTree[] trees) {
int numTrees = trees.length;
ElementTree[] sorted = new ElementTree[numTrees];
Map<ElementTree, List<Integer>> table = new HashMap<>(numTrees * 2 + 1);
for (int i = 0; i < trees.length; i++) {
List<Integer> indices = table.get(trees[i]);
if (indices == null) {
indices = new ArrayList<>(10);
table.put(trees[i], indices);
}
indices.add(i);
}
ElementTree oldest = trees[ElementTree.findOldest(trees)];
int i = numTrees - 1;
while (i >= 0) {
List<Integer> indices = table.remove(oldest);
for (Enumeration<Integer> e = Collections.enumeration(indices); e.hasMoreElements();) {
e.nextElement();
sorted[i] = oldest;
i--;
}
if (i >= 0) {
ElementTree parent = oldest.getParent();
while (parent != null && table.get(parent) == null) {
parent = parent.getParent();
}
if (parent == null) {
Exception e = new NullPointerException("null parent found while collapsing trees");
IStatus status = new Status(IStatus.WARNING, ResourcesPlugin.PI_RESOURCES, IResourceStatus.INTERNAL_ERROR, e.getMessage(), e);
Policy.log(status);
return null;
}
oldest = parent;
}
}
return sorted;
}
@Override
public void startup(IProgressMonitor monitor) throws CoreException {
restore(monitor);
java.io.File table = workspace.getMetaArea().getSafeTableLocationFor(ResourcesPlugin.PI_RESOURCES).toFile();
if (!table.exists())
table.getParentFile().mkdirs();
}
protected void updateDeltaExpiration(String pluginId) {
String key = DELTA_EXPIRATION_PREFIX + pluginId;
if (!masterTable.containsKey(key))
masterTable.setProperty(key, Long.toString(System.currentTimeMillis()));
}
private void validateMasterTableBeforeSave(java.io.File target) throws IOException {
if (target.exists()) {
MasterTable previousMasterTable = new MasterTable();
try (
SafeChunkyInputStream input = new SafeChunkyInputStream(target);
) {
previousMasterTable.load(input);
String stringValue = previousMasterTable.getProperty(ROOT_SEQUENCE_NUMBER_KEY);
if (stringValue != null) {
int valueInFile = Integer.parseInt(stringValue);
int valueInMemory = Integer.parseInt(masterTable.getProperty(ROOT_SEQUENCE_NUMBER_KEY));
if (valueInMemory < valueInFile) {
String message = getBadSequenceNumberErrorMessage(target, valueInFile, valueInMemory, masterTable, previousMasterTable);
Assert.isLegal(false, message);
}
}
}
}
}
private static String getBadSequenceNumberErrorMessage(java.io.File target, int valueInFile, int valueInMemory, MasterTable currentMasterTable, MasterTable previousMasterTable) {
StringBuilder messageBuffer = new StringBuilder();
messageBuffer.append("Cannot set lower sequence number for root (previous: ");
messageBuffer.append(valueInFile);
messageBuffer.append(", new: ");
messageBuffer.append(valueInMemory);
messageBuffer.append("). Location: ");
messageBuffer.append(target.getAbsolutePath());
try {
messageBuffer.append("Timestamps and tree sequence numbers from file:");
java.nio.file.Path targetPath = Paths.get(target.getAbsolutePath());
List<String> masterTableFileContents = Files.readAllLines(targetPath, Charset.defaultCharset());
for (String line : masterTableFileContents) {
if (line != null) {
boolean isPropertiesTimestamp = line.startsWith("#");
boolean isTreeProperty = line.startsWith(ROOT_SEQUENCE_NUMBER_KEY);
if (isPropertiesTimestamp || isTreeProperty) {
messageBuffer.append(System.lineSeparator());
messageBuffer.append(line);
}
}
}
} catch (IOException e) {
ILog log = ResourcesPlugin.getPlugin().getLog();
String errorMessage = "Error occurred while reading master table file";
IStatus errorStatus = new Status(IStatus.ERROR, ResourcesPlugin.PI_RESOURCES, errorMessage, e);
log.log(errorStatus);
}
return messageBuffer.toString();
}
public void visitAndSave(final IResource root) throws CoreException {
Assert.isLegal(root.getType() == IResource.ROOT || root.getType() == IResource.PROJECT);
if (!root.isAccessible())
return;
final Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
final MarkerManager markerManager = workspace.getMarkerManager();
IPath markersLocation = workspace.getMetaArea().getMarkersLocationFor(root);
IPath markersTempLocation = workspace.getMetaArea().getBackupLocationFor(markersLocation);
IPath syncInfoLocation = workspace.getMetaArea().getSyncInfoLocationFor(root);
IPath syncInfoTempLocation = workspace.getMetaArea().getBackupLocationFor(syncInfoLocation);
final List<String> writtenTypes = new ArrayList<>(5);
final List<QualifiedName> writtenPartners = new ArrayList<>(synchronizer.registry.size());
DataOutputStream o1 = null;
DataOutputStream o2 = null;
String message;
try {
o1 = new DataOutputStream(new SafeFileOutputStream(markersLocation.toOSString(), markersTempLocation.toOSString()));
if (root.getType() != IResource.ROOT)
o2 = new DataOutputStream(new SafeFileOutputStream(syncInfoLocation.toOSString(), syncInfoTempLocation.toOSString()));
} catch (IOException e) {
FileUtil.safeClose(o1);
FileUtil.safeClose(o2);
message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
}
final DataOutputStream markersOutput = o1;
final DataOutputStream syncInfoOutput = o2;
final long[] saveTimes = new long[2];
IElementContentVisitor visitor = (tree, requestor, elementContents) -> {
ResourceInfo info = (ResourceInfo) elementContents;
if (info != null) {
try {
long start = System.currentTimeMillis();
markerManager.save(info, requestor, markersOutput, writtenTypes);
long markerSaveTime = System.currentTimeMillis() - start;
saveTimes[0] += markerSaveTime;
persistMarkers += markerSaveTime;
if (syncInfoOutput != null) {
start = System.currentTimeMillis();
synchronizer.saveSyncInfo(info, requestor, syncInfoOutput, writtenPartners);
long syncInfoSaveTime = System.currentTimeMillis() - start;
saveTimes[1] += syncInfoSaveTime;
persistSyncInfo += syncInfoSaveTime;
}
} catch (IOException e) {
throw new WrappedRuntimeException(e);
}
}
return root.getType() != IResource.ROOT;
};
try {
try {
new ElementTreeIterator(workspace.getElementTree(), root.getFullPath()).iterate(visitor);
} catch (WrappedRuntimeException e) {
throw (IOException) e.getTargetException();
}
if (Policy.DEBUG_SAVE_MARKERS)
Policy.debug("Save Markers for " + root.getFullPath() + ": " + saveTimes[0] + "ms");
if (Policy.DEBUG_SAVE_SYNCINFO)
Policy.debug("Save SyncInfo for " + root.getFullPath() + ": " + saveTimes[1] + "ms");
removeGarbage(markersOutput, markersLocation, markersTempLocation);
if (syncInfoOutput != null) {
removeGarbage(syncInfoOutput, syncInfoLocation, syncInfoTempLocation);
syncInfoOutput.close();
}
markersOutput.close();
} catch (IOException e) {
message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
} finally {
FileUtil.safeClose(markersOutput);
FileUtil.safeClose(syncInfoOutput);
}
if (root.getType() == IResource.PROJECT)
return;
IProject[] projects = ((IWorkspaceRoot) root).getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject project : projects)
visitAndSave(project);
}
public void visitAndSnap(final IResource root) throws CoreException {
Assert.isLegal(root.getType() == IResource.ROOT || root.getType() == IResource.PROJECT);
if (!root.isAccessible())
return;
final Synchronizer synchronizer = (Synchronizer) workspace.getSynchronizer();
final MarkerManager markerManager = workspace.getMarkerManager();
IPath markersLocation = workspace.getMetaArea().getMarkersSnapshotLocationFor(root);
IPath syncInfoLocation = workspace.getMetaArea().getSyncInfoSnapshotLocationFor(root);
SafeChunkyOutputStream safeMarkerStream = null;
SafeChunkyOutputStream safeSyncInfoStream = null;
DataOutputStream o1 = null;
DataOutputStream o2 = null;
String message;
try {
safeMarkerStream = new SafeChunkyOutputStream(markersLocation.toFile());
o1 = new DataOutputStream(safeMarkerStream);
if (root.getType() != IResource.ROOT) {
safeSyncInfoStream = new SafeChunkyOutputStream(syncInfoLocation.toFile());
o2 = new DataOutputStream(safeSyncInfoStream);
}
} catch (IOException e) {
FileUtil.safeClose(o1);
message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
}
final DataOutputStream markersOutput = o1;
final DataOutputStream syncInfoOutput = o2;
int markerFileSize = markersOutput.size();
int syncInfoFileSize = safeSyncInfoStream == null ? -1 : syncInfoOutput.size();
final long[] snapTimes = new long[2];
IElementContentVisitor visitor = (tree, requestor, elementContents) -> {
ResourceInfo info = (ResourceInfo) elementContents;
if (info != null) {
try {
long start = System.currentTimeMillis();
markerManager.snap(info, requestor, markersOutput);
long markerSnapTime = System.currentTimeMillis() - start;
snapTimes[0] += markerSnapTime;
persistMarkers += markerSnapTime;
if (syncInfoOutput != null) {
start = System.currentTimeMillis();
synchronizer.snapSyncInfo(info, requestor, syncInfoOutput);
long syncInfoSnapTime = System.currentTimeMillis() - start;
snapTimes[1] += syncInfoSnapTime;
persistSyncInfo += syncInfoSnapTime;
}
} catch (IOException e) {
throw new WrappedRuntimeException(e);
}
}
return root.getType() != IResource.ROOT;
};
try {
try {
new ElementTreeIterator(workspace.getElementTree(), root.getFullPath()).iterate(visitor);
} catch (WrappedRuntimeException e) {
throw (IOException) e.getTargetException();
}
if (Policy.DEBUG_SAVE_MARKERS)
Policy.debug("Snap Markers for " + root.getFullPath() + ": " + snapTimes[0] + "ms");
if (Policy.DEBUG_SAVE_SYNCINFO)
Policy.debug("Snap SyncInfo for " + root.getFullPath() + ": " + snapTimes[1] + "ms");
if (markerFileSize != markersOutput.size())
safeMarkerStream.succeed();
if (safeSyncInfoStream != null && syncInfoFileSize != syncInfoOutput.size()) {
safeSyncInfoStream.succeed();
syncInfoOutput.close();
}
markersOutput.close();
} catch (IOException e) {
message = NLS.bind(Messages.resources_writeMeta, root.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, root.getFullPath(), message, e);
} finally {
FileUtil.safeClose(markersOutput);
FileUtil.safeClose(syncInfoOutput);
}
if (root.getType() == IResource.PROJECT)
return;
IProject[] projects = ((IWorkspaceRoot) root).getProjects(IContainer.INCLUDE_HIDDEN);
for (IProject project : projects)
visitAndSnap(project);
}
private void writeBuilderPersistentInfo(DataOutputStream output, List<BuilderPersistentInfo> builders, IProgressMonitor monitor) throws IOException {
int numBuilders = builders.size();
output.writeInt(numBuilders);
for (int i = 0; i < numBuilders; i++) {
BuilderPersistentInfo info = builders.get(i);
output.writeUTF(info.getProjectName());
output.writeUTF(info.getBuilderName());
IProject[] interestingProjects = info.getInterestingProjects();
output.writeInt(interestingProjects.length);
for (IProject interestingProject : interestingProjects)
output.writeUTF(interestingProject.getName());
}
}
@Override
public void writeElement(IPath path, Object element, DataOutput output) throws IOException {
Assert.isNotNull(path);
Assert.isNotNull(element);
Assert.isNotNull(output);
ResourceInfo info = (ResourceInfo) element;
output.writeInt(info.getFlags());
info.writeTo(output);
}
private void getTreesToSave(IProject project, List<ElementTree> trees, List<BuilderPersistentInfo> builderInfos, List<String> configNames, List<ElementTree> additionalTrees, List<BuilderPersistentInfo> additionalBuilderInfos, List<String> additionalConfigNames) throws CoreException {
if (project.isOpen()) {
String activeConfigName = project.getActiveBuildConfig().getName();
List<BuilderPersistentInfo> infos = workspace.getBuildManager().createBuildersPersistentInfo(project);
if (infos != null) {
for (BuilderPersistentInfo info : infos) {
if (info.getLastBuiltTree() == null)
continue;
String configName = info.getConfigName() == null ? activeConfigName : info.getConfigName();
if (configName.equals(activeConfigName)) {
builderInfos.add(info);
configNames.add(configName);
trees.add(info.getLastBuiltTree());
} else {
additionalBuilderInfos.add(info);
additionalConfigNames.add(configName);
additionalTrees.add(info.getLastBuiltTree());
}
}
}
}
}
protected void writeTree(Map<String, ElementTree> statesToSave, DataOutputStream output, IProgressMonitor monitor)
throws IOException, CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
boolean wasImmutable = false;
try {
ElementTree current = workspace.getElementTree();
wasImmutable = current.isImmutable();
current.immutable();
ArrayList<ElementTree> trees = new ArrayList<>(statesToSave.size() * 2);
subMonitor.worked(1);
writeWorkspaceFields(output, subMonitor.newChild(2));
output.writeInt(statesToSave.size());
for (Map.Entry<String, ElementTree> entry : statesToSave.entrySet()) {
String pluginId = entry.getKey();
output.writeUTF(pluginId);
trees.add(entry.getValue());
updateDeltaExpiration(pluginId);
}
subMonitor.worked(1);
IProject[] projects = workspace.getRoot().getProjects(IContainer.INCLUDE_HIDDEN);
List<BuilderPersistentInfo> builderInfos = new ArrayList<>(projects.length * 2);
List<String> configNames = new ArrayList<>(projects.length);
List<ElementTree> additionalTrees = new ArrayList<>(projects.length * 2);
List<BuilderPersistentInfo> additionalBuilderInfos = new ArrayList<>(projects.length * 2);
List<String> additionalConfigNames = new ArrayList<>(projects.length);
for (IProject project : projects)
getTreesToSave(project, trees, builderInfos, configNames, additionalTrees, additionalBuilderInfos,
additionalConfigNames);
writeBuilderPersistentInfo(output, builderInfos, subMonitor.newChild(1));
trees.addAll(additionalTrees);
trees.add(current);
ElementTreeWriter writer = new ElementTreeWriter(this);
ElementTree[] treesToSave = trees.toArray(new ElementTree[trees.size()]);
writer.writeDeltaChain(treesToSave, Path.ROOT, ElementTreeWriter.D_INFINITE, output,
ResourceComparator.getSaveComparator());
subMonitor.worked(4);
writeBuilderPersistentInfo(output, additionalBuilderInfos, subMonitor.newChild(1));
for (String string : configNames)
output.writeUTF(string);
for (String string : additionalConfigNames)
output.writeUTF(string);
} finally {
subMonitor.done();
if (!wasImmutable)
workspace.newWorkingTree();
}
}
protected void writeTree(Project project, DataOutputStream output, IProgressMonitor monitor)
throws IOException, CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, 10);
boolean wasImmutable = false;
try {
ElementTree current = workspace.getElementTree();
wasImmutable = current.isImmutable();
current.immutable();
List<ElementTree> trees = new ArrayList<>(2);
subMonitor.worked(1);
List<String> configNames = new ArrayList<>(5);
List<BuilderPersistentInfo> builderInfos = new ArrayList<>(5);
List<String> additionalConfigNames = new ArrayList<>(5);
List<BuilderPersistentInfo> additionalBuilderInfos = new ArrayList<>(5);
List<ElementTree> additionalTrees = new ArrayList<>(5);
getTreesToSave(project, trees, builderInfos, configNames, additionalTrees, additionalBuilderInfos,
additionalConfigNames);
writeBuilderPersistentInfo(output, builderInfos, subMonitor.newChild(2));
trees.addAll(additionalTrees);
trees.add(current);
ElementTreeWriter writer = new ElementTreeWriter(this);
ElementTree[] treesToSave = trees.toArray(new ElementTree[trees.size()]);
writer.writeDeltaChain(treesToSave, project.getFullPath(), ElementTreeWriter.D_INFINITE, output,
ResourceComparator.getSaveComparator());
subMonitor.worked(5);
writeBuilderPersistentInfo(output, additionalBuilderInfos, subMonitor.newChild(2));
for (String string : configNames)
output.writeUTF(string);
for (String string : additionalConfigNames)
output.writeUTF(string);
} finally {
subMonitor.done();
if (!wasImmutable)
workspace.newWorkingTree();
}
}
protected void writeTree(Project project, int depth) throws CoreException {
long start = System.currentTimeMillis();
IPath treeLocation = workspace.getMetaArea().getTreeLocationFor(project, true);
IPath tempLocation = workspace.getMetaArea().getBackupLocationFor(treeLocation);
try {
SafeFileOutputStream safe = new SafeFileOutputStream(treeLocation.toOSString(), tempLocation.toOSString());
try (
DataOutputStream output = new DataOutputStream(safe);
) {
output.writeInt(ICoreConstants.WORKSPACE_TREE_VERSION_2);
writeTree(project, output, null);
}
} catch (IOException e) {
String msg = NLS.bind(Messages.resources_writeMeta, project.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, treeLocation, msg, e);
}
if (Policy.DEBUG_SAVE_TREE)
Policy.debug("Save tree for " + project.getFullPath() + ": " + (System.currentTimeMillis() - start) + "ms");
}
protected void writeWorkspaceFields(DataOutputStream output, IProgressMonitor monitor) throws IOException {
output.writeLong(workspace.nextNodeId);
output.writeLong(0L);
output.writeLong(workspace.nextMarkerId);
((Synchronizer) workspace.getSynchronizer()).savePartners(output);
}
}