Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,12 @@ protected boolean refreshResource(IResource target, int depth, boolean updateAli
SubMonitor refreshMonitor = subMonitor.newChild(98);
RefreshLocalVisitor visitor = updateAliases ? new RefreshLocalAliasVisitor(refreshMonitor) : new RefreshLocalVisitor(refreshMonitor);
tree.accept(visitor, depth);
// a project without description file is closed rather than recreating the file
for (Project project : visitor.getProjectsWithoutDescription()) {
if (project.isOpen()) {
project.basicClose(null);
}
}
IStatus result = visitor.getErrorStatus();
if (!result.isOK()) {
throw new ResourceException(result);
Expand Down Expand Up @@ -1487,42 +1493,6 @@ public void write(IFolder target, boolean force, IProgressMonitor monitor) throw
updateLocalSync(info, store.fetchInfo().getLastModified());
}

/**
Comment thread
vogella marked this conversation as resolved.
* Write the .project file without modifying the resource tree. This is called
* during save when it is discovered that the .project file is missing. The tree
* cannot be modified during save.
*/
public void writeSilently(IProject target) throws CoreException {
IPath location = locationFor(target, false);
//if the project location cannot be resolved, we don't know if a description file exists or not
if (location == null) {
return;
}
IFileStore projectStore = getStore(target);
projectStore.mkdir(EFS.NONE, null);
//can't do anything if there's no description
IProjectDescription desc = ((Project) target).internalGetDescription();
if (desc == null) {
return;
}
//write the project's private description to the meta-data area
getWorkspace().getMetaArea().writePrivateDescription(target);

//write the file that represents the project description
IFileStore fileStore = projectStore.getChild(IProjectDescription.DESCRIPTION_FILE_NAME);
try (
OutputStream out = fileStore.openOutputStream(EFS.NONE, null)
) {
IFile file = target.getFile(IProjectDescription.DESCRIPTION_FILE_NAME);
new ModelObjectWriter().write(desc, out, file.getLineSeparator(true));
} catch (IOException e) {
String msg = NLS.bind(Messages.resources_writeMeta, target.getFullPath());
throw new ResourceException(IResourceStatus.FAILED_WRITE_METADATA, target.getFullPath(), msg, e);
}
//for backwards compatibility, ensure the old .prj file is deleted
getWorkspace().getMetaArea().clearOldDescription(target);
}

public boolean storeHistory(IResource file) {
WorkspaceDescription description = workspace.internalGetDescription();
return (description.isKeepDerivedState() || !file.isDerived()) && !disableHistory(file);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
*******************************************************************************/
package org.eclipse.core.internal.localstore;

import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.core.internal.resources.Container;
import org.eclipse.core.internal.resources.File;
import org.eclipse.core.internal.resources.Folder;
import org.eclipse.core.internal.resources.ICoreConstants;
import org.eclipse.core.internal.resources.Project;
import org.eclipse.core.internal.resources.Resource;
import org.eclipse.core.internal.resources.ResourceInfo;
import org.eclipse.core.internal.resources.ResourceStatus;
Expand Down Expand Up @@ -56,6 +59,7 @@ public class RefreshLocalVisitor implements IUnifiedTreeVisitor, ILocalStoreCons
protected SubMonitor monitor;
protected boolean resourceChanged;
protected Workspace workspace;
private final Set<Project> projectsWithoutDescription = new LinkedHashSet<>();

public RefreshLocalVisitor(IProgressMonitor monitor) {
this.monitor = SubMonitor.convert(monitor);
Expand Down Expand Up @@ -146,6 +150,13 @@ protected void folderToFile(UnifiedTreeNode node, Resource target) throws CoreEx
target.getLocalManager().updateLocalSync(info, node.getLastModified());
}

/**
* Returns the open projects whose description file was found deleted.
*/
public Set<Project> getProjectsWithoutDescription() {
return projectsWithoutDescription;
}

/**
* Returns the status of the nodes visited so far. This will be a multi-status
* that describes all problems that have occurred, or an OK status if everything
Expand Down Expand Up @@ -314,13 +325,22 @@ public boolean visit(UnifiedTreeNode node) throws CoreException {
errors.merge(new ResourceStatus(IResourceStatus.INVALID_RESOURCE_NAME, message));
return false;
}
boolean existedInWorkspace = node.existsInWorkspace();
int state = synchronizeExistence(node, target);
if (state == RL_IN_SYNC || state == RL_NOT_IN_SYNC) {
if (targetType == IResource.FILE) {
try {
((File) target).updateMetadataFiles();
} catch (CoreException e) {
errors.merge(e.getStatus());
File file = (File) target;
Project project = (Project) file.getProject();
// a filtered description file is still read from disk
if (existedInWorkspace && !file.exists() && file.isProjectDescriptionFile()
&& !file.getLocalManager().hasSavedDescription(project)) {
projectsWithoutDescription.add(project);
} else {
try {
file.updateMetadataFiles();
} catch (CoreException e) {
errors.merge(e.getStatus());
}
}
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ public void setContents(byte[] content, int updateFlags, IProgressMonitor monito
public long setLocalTimeStamp(long value) throws CoreException {
//override to handle changing timestamp on project description file
long result = super.setLocalTimeStamp(value);
if (path.segmentCount() == 2 && path.segment(1).equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
if (isProjectDescriptionFile()) {
//handle concurrent project deletion
ResourceInfo projectInfo = ((Project) getProject()).getResourceInfo(false, false);
if (projectInfo != null) {
Expand All @@ -576,10 +576,7 @@ public long setLocalTimeStamp(long value) throws CoreException {
* been modified (added, removed, or changed).
*/
public void updateMetadataFiles() throws CoreException {
int count = path.segmentCount();
String name = path.segment(1);
// is this a project description file?
if (count == 2 && name.equals(IProjectDescription.DESCRIPTION_FILE_NAME)) {
if (isProjectDescriptionFile()) {
Project project = (Project) getProject();
project.updateDescription();
// Discard stale project natures on ProjectInfo
Expand All @@ -588,12 +585,19 @@ public void updateMetadataFiles() throws CoreException {
return;
}
// check to see if we are in the .settings directory
if (count == 3 && EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(name)) {
if (path.segmentCount() == 3 && EclipsePreferences.DEFAULT_PREFERENCES_DIRNAME.equals(path.segment(1))) {
ProjectPreferences.updatePreferences(this);
return;
}
}

/**
* Returns whether this file is the description file (.project) of its project.
*/
public boolean isProjectDescriptionFile() {
return path.segmentCount() == 2 && path.segment(1).equals(IProjectDescription.DESCRIPTION_FILE_NAME);
}

@Deprecated
@Override
public void setCharset(String newCharset) throws CoreException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,8 @@ public void close(IProgressMonitor monitor) throws CoreException {
if (!isOpen(flags)) {
return;
}
// Signal that this resource is about to be closed. Do this at the very
// beginning so that infrastructure pieces have a chance to do clean up
// while the resources still exist.
workspace.beginOperation(true);
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, this));
// flush the build order early in case there is a problem
workspace.flushBuildOrder();
IProgressMonitor sub = subMonitor.newChild(49, SubMonitor.SUPPRESS_SUBTASK);
IStatus saveStatus = workspace.getSaveManager().save(ISaveContext.PROJECT_SAVE, this, sub);
internalClose(subMonitor.newChild(49));
if (saveStatus != null && !saveStatus.isOK()) {
throw new ResourceException(saveStatus);
}
basicClose(subMonitor.newChild(98));
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
throw e;
Expand Down Expand Up @@ -670,6 +659,26 @@ private boolean shouldBuild() {
workspace.run(buildRunnable, null, IWorkspace.AVOID_UPDATE, monitor);
}

/**
* Closes this open project. Must be called from within a workspace operation
* whose scheduling rule covers this project.
*/
public void basicClose(IProgressMonitor monitor) throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor, 2);
// Signal that this resource is about to be closed. Do this at the very
// beginning so that infrastructure pieces have a chance to do clean up
// while the resources still exist.
workspace.broadcastEvent(LifecycleEvent.newEvent(LifecycleEvent.PRE_PROJECT_CLOSE, this));
// flush the build order early in case there is a problem
workspace.flushBuildOrder();
IProgressMonitor sub = subMonitor.newChild(1, SubMonitor.SUPPRESS_SUBTASK);
IStatus saveStatus = workspace.getSaveManager().save(ISaveContext.PROJECT_SAVE, this, sub);
internalClose(subMonitor.newChild(1));
if (saveStatus != null && !saveStatus.isOK()) {
throw new ResourceException(saveStatus);
}
}

/**
* Closes the project. This is called during restore when there is a failure
* to read the project description. Since it is called during workspace restore,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,12 @@ public void delete(int updateFlags, IProgressMonitor monitor) throws CoreExcepti
((Rules) workspace.getRuleFactory()).setRuleFactory((IProject) this, null);
// Make sure project deletion is remembered.
workspace.getSaveManager().requestSnapshot();
} else if (getType() == FILE && ((File) this).isProjectDescriptionFile()) {
// a project without description file is closed rather than recreating the file
Project project = (Project) getProject();
if (project.isOpen() && !getLocalManager().hasSavedDescription(project)) {
project.basicClose(progress.split(1));
}
}
} catch (OperationCanceledException e) {
workspace.getWorkManager().operationCanceled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1114,27 +1114,32 @@ public void standardMoveProject(IProject source, IProjectDescription description
}

// Move the project content in the local file system.
boolean contentMoved = true;
try {
moveProjectContent(source, destinationStore, flags, Policy.subMonitorFor(monitor, Policy.totalWork * 3 / 4));
} catch (CoreException e) {
message = NLS.bind(Messages.localstore_couldNotMove, source.getFullPath());
IStatus status = new ResourceStatus(IStatus.ERROR, source.getFullPath(), message, e);
failed(status);
//refresh the project because it might have been partially moved
try {
source.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e2) {
//ignore secondary failures
}
contentMoved = false;
}

// If we got this far the project content has been moved on disk (if necessary)
// and we need to update the workspace tree.
movedProjectSubtree(source, description);
monitor.worked(Policy.totalWork * 1 / 8);

IProject destination = source.getWorkspace().getRoot().getProject(description.getName());
if (!contentMoved) {
// the content might have been partially moved, so align the tree with the destination
try {
destination.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
//ignore secondary failures
}
}
boolean isDeep = (flags & IResource.SHALLOW) == 0;
updateTimestamps(source.getWorkspace().getRoot().getProject(description.getName()), isDeep);
updateTimestamps(destination, isDeep);
monitor.worked(Policy.totalWork * 1 / 8);
} finally {
lock.release();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1311,8 +1311,9 @@
workspace.getFileSystemManager().getHistoryStore().clean(Policy.subMonitorFor(monitor, 1));
monitor.ignoreCancelState(keepConsistencyWhenCanceled);

// write out all metainfo (e.g., workspace/project descriptions)
saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
// write out the workspace metainfo (e.g., the workspace description)
saveMetaInfo();
monitor.worked(1);
break;
case ISaveContext.SNAPSHOT :
snapTree(workspace.getElementTree(), Policy.subMonitorFor(monitor, 1));
Expand All @@ -1327,8 +1328,9 @@
}
collapseTrees(contexts);
clearSavedDelta();
// write out all metainfo (e.g., workspace/project descriptions)
saveMetaInfo(warnings, Policy.subMonitorFor(monitor, 1));
// write out the workspace metainfo (e.g., the workspace description)
saveMetaInfo();
monitor.worked(1);
break;
case ISaveContext.PROJECT_SAVE :
writeTree(project, IResource.DEPTH_INFINITE);
Expand All @@ -1338,10 +1340,6 @@
monitor.worked(1);
// reset the snapshot file
resetSnapshots(project);
IStatus result = saveMetaInfo(project, null);
if (!result.isOK()) {
warnings.merge(result);
}
monitor.worked(1);
break;
}
Expand Down Expand Up @@ -1404,52 +1402,20 @@
}

/**
* Writes the metainfo (e.g. descriptions) of the given workspace and
* all projects to the local disk.
* Writes the metainfo (e.g. description) of the workspace to the local disk.
*/
protected void saveMetaInfo(MultiStatus problems, IProgressMonitor monitor) throws CoreException {
protected void saveMetaInfo() {
if (Policy.DEBUG_SAVE_METAINFO) {
Policy.debug("Save workspace metainfo: starting..."); //$NON-NLS-1$
}
long start = System.currentTimeMillis();
// save preferences (workspace description, path variables, etc)
ResourcesPlugin.getPlugin().savePluginPreferences();

Check warning on line 1413 in resources/bundles/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/SaveManager.java

View check run for this annotation

Jenkins - Eclipse Platform / Compiler

Deprecation

NORMAL: The method savePluginPreferences() from the type Plugin is deprecated
// save projects' meta info
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"); //$NON-NLS-1$ //$NON-NLS-2$
}
}

/**
* Ensures that the project meta-info is saved. The project meta-info
* is usually saved as soon as it changes, so this is just a sanity check
* to make sure there is something on disk before we shutdown.
*
* @return Status object containing non-critical warnings, or an OK status.
*/
protected IStatus saveMetaInfo(Project project, IProgressMonitor monitor) throws CoreException {
long start = System.currentTimeMillis();
//if there is nothing on disk, write the description
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"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
return Status.OK_STATUS;
}

/**
* Writes a snapshot of project refresh information to the specified
* location.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@
import static org.eclipse.core.tests.resources.ResourceTestUtil.createInWorkspace;
import static org.eclipse.core.tests.resources.ResourceTestUtil.removeFromFileSystem;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.eclipse.core.internal.resources.ICoreConstants;
import org.eclipse.core.internal.resources.TestingSupport;
import org.eclipse.core.internal.resources.Workspace;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
Expand Down Expand Up @@ -61,9 +59,6 @@ private boolean existsInFileSystemWithNoContent(IResource resource) {

@Test
public void testProjectDeletion() throws CoreException {
//snapshot will recreate the deleted .project file
TestingSupport.waitForSnapshot();

// create resources
IResource[] resources = buildResources(project, "/File1", "/Folder1/", "/Folder1/File1", "/Folder1/Folder2/");
createInWorkspace(resources);
Expand All @@ -72,11 +67,12 @@ public void testProjectDeletion() throws CoreException {
Workspace.clear(project.getLocation().toFile());

// run synchronize
//The .project file has been deleted, so this will fail
assertThrows(CoreException.class, () -> project.refreshLocal(IResource.DEPTH_INFINITE, null));
// The .project file has been deleted, so this closes the project
project.refreshLocal(IResource.DEPTH_INFINITE, null);

/* project should still exists */
/* project should still exist but be closed */
assertTrue(project.exists());
assertFalse(project.isOpen());

/* resources should not exist anymore */
for (int i = 1; i < resources.length; i++) {
Expand Down
Loading
Loading