blob: c062b6a193491d066bed837123fd59260d356109 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.internal.ccvs.core.resources;
import java.util.*;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.*;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.*;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
/**
* This class can be used to fetch and cache file contents for remote files.
*/
public class UpdateContentCachingService implements IUpdateMessageListener {
private CVSRepositoryLocation repository;
private ICVSFolder remoteRoot;
private final CVSTag tag;
private final int depth;
private boolean fetchAbsentDirectories = true;
private ArrayList<ICVSResource> removed = new ArrayList<>();
public class SandboxUpdate extends Update {
@Override
protected boolean shouldRetrieveAbsentDirectories(Session session) {
return fetchAbsentDirectories;
}
@Override
protected IStatus commandFinished(Session session, GlobalOption[] globalOptions, LocalOption[] localOptions, ICVSResource[] resources, IProgressMonitor monitor, IStatus status) throws CVSException {
// Don't do anything (i.e. don't prune)
return status;
}
@Override
protected IStatus doExecute(Session session, GlobalOption[] globalOptions, LocalOption[] localOptions, String[] arguments, ICommandOutputListener listener, IProgressMonitor monitor) throws CVSException {
session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_CREATED));
session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_MERGED));
session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_UPDATE_EXISTING));
session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_UPDATED));
return super.doExecute(session, globalOptions, localOptions, arguments, listener, monitor);
}
}
/**
* This class overrides the "Created" handler in order to configure the remote file
* to receive and cache the contents
*/
public class SandboxUpdatedHandler extends UpdatedHandler {
public SandboxUpdatedHandler(int type) {
super(type);
}
@Override
protected void receiveTargetFile(
Session session,
ICVSFile mFile,
String entryLine,
Date modTime,
boolean binary,
boolean readOnly,
boolean executable,
IProgressMonitor monitor)
throws CVSException {
// Set the sync info first so that the contents are cached properly
ResourceSyncInfo info = new ResourceSyncInfo(entryLine, modTime);
// We're always excepting new revisions so the file is clean
mFile.setSyncInfo(info, ICVSFile.CLEAN);
// receive the file contents from the server
session.receiveFile(mFile, binary, getHandlerType(), monitor);
// Handle execute
try {
if (executable) mFile.setExecutable(true);
} catch (CVSException e) {
// Just log and keep going
CVSProviderPlugin.log(e);
}
}
}
public static RemoteFolder buildRemoteTree(final CVSRepositoryLocation repository, ICVSFolder root, CVSTag tag, int depth, IProgressMonitor monitor) throws CVSException {
monitor.beginTask(null, 100);
try {
RemoteFolder tree = buildBaseTree(repository, root, tag, Policy.subMonitorFor(monitor, 50));
UpdateContentCachingService service = new UpdateContentCachingService(repository, tree, tag, depth);
service.setFetchAbsentDirectories(getFetchAbsentDirectories(root));
if (!service.cacheFileContents(Policy.subMonitorFor(monitor, 50)))
return null;
return tree;
} finally {
monitor.done();
}
}
private void setFetchAbsentDirectories(boolean fetchAbsentDirectories) {
this.fetchAbsentDirectories = fetchAbsentDirectories;
}
private static boolean getFetchAbsentDirectories(ICVSFolder root) {
IResource resource = root.getIResource();
if (resource != null) {
IProject project = resource.getProject();
RepositoryProvider provider = RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId());
if (provider instanceof CVSTeamProvider) {
CVSTeamProvider cp = (CVSTeamProvider) provider;
try {
return cp.getFetchAbsentDirectories();
} catch (CVSException e) {
CVSProviderPlugin.log(e);
}
}
}
return CVSProviderPlugin.getPlugin().getFetchAbsentDirectories();
}
private static RemoteFolder buildBaseTree(final CVSRepositoryLocation repository, ICVSFolder root, CVSTag tag, IProgressMonitor progress) throws CVSException {
try {
RemoteFolderTreeBuilder builder = new RemoteFolderTreeBuilder(repository, root, tag) {
protected RemoteFolder createRemoteFolder(ICVSFolder local, RemoteFolder parent, FolderSyncInfo folderSyncInfo) {
return new RemoteFolderSandbox(parent, local.getName(), repository, folderSyncInfo.getRepository(), folderSyncInfo.getTag(), folderSyncInfo.getIsStatic());
}
protected RemoteFile createRemoteFile(RemoteFolder remote, byte[] syncBytes) throws CVSException {
return new RemoteFile(remote, syncBytes){
public boolean isModified(IProgressMonitor monitor) throws CVSException {
return false;
}
public void delete() {
RemoteFolderSandbox parent = (RemoteFolderSandbox)getParent();
parent.remove(this);
}
};
}
protected boolean isPruneEmptyDirectories() {
return true;
}
};
progress.beginTask(null, 100);
IProgressMonitor subProgress = Policy.infiniteSubMonitorFor(progress, 100);
subProgress.beginTask(null, 512);
subProgress.subTask(NLS.bind(CVSMessages.RemoteFolderTreeBuilder_buildingBase, new String[] { root.getName() }));
RemoteFolder tree = builder.buildBaseTree(null, root, subProgress);
if (tree == null) {
// The local tree is empty and was pruned.
// Return the root folder so that the operation can proceed
FolderSyncInfo folderSyncInfo = root.getFolderSyncInfo();
if (folderSyncInfo == null) return null;
return new RemoteFolderSandbox(null, root.getName(), repository, folderSyncInfo.getRepository(), folderSyncInfo.getTag(), folderSyncInfo.getIsStatic());
}
return tree;
} finally {
progress.done();
}
}
public static RemoteFile buildRemoteTree(CVSRepositoryLocation repository, ICVSFile file, CVSTag tag, IProgressMonitor monitor) throws CVSException {
monitor.beginTask(null, 100);
try {
RemoteFolderTreeBuilder builder = new RemoteFolderTreeBuilder(repository, file.getParent(), tag);
RemoteFile remote = builder.buildTree(file, Policy.subMonitorFor(monitor, 10));
if (remote == null)
return null;
byte[] syncBytes = remote.getSyncBytes();
if (builder.getFileDiffs().length > 0) {
// Getting the storage of the file will cache the contents
remote.getStorage(Policy.subMonitorFor(monitor, 90));
}
// We need to set the sync bytes back because the content fetch
// makes the handle sticky
remote.setSyncBytes(syncBytes, ICVSFile.CLEAN);
return remote;
} catch (TeamException e) {
throw CVSException.wrapException(e);
} finally {
monitor.done();
}
}
public UpdateContentCachingService(CVSRepositoryLocation repository, RemoteFolder tree, CVSTag tag, int depth) {
this.repository = repository;
this.remoteRoot = tree;
this.tag = tag;
this.depth = depth;
}
private boolean cacheFileContents(IProgressMonitor monitor) throws CVSException {
// Fetch the file contents for all out-of-sync files by running an update
// on the remote tree passing the known changed files as arguments
monitor.beginTask(null, 100);
Policy.checkCanceled(monitor);
Session session = new Session(repository, remoteRoot, false);
session.open(Policy.subMonitorFor(monitor, 10), false /* read-only */);
try {
Policy.checkCanceled(monitor);
IStatus status = new SandboxUpdate().execute(session,
Command.NO_GLOBAL_OPTIONS,
getLocalOptions(),
new String[] { Session.CURRENT_LOCAL_FOLDER },
new UpdateListener(this),
Policy.subMonitorFor(monitor, 90));
if (!status.isOK()) {
if (status.getCode() == CVSStatus.SERVER_ERROR) {
CVSServerException e = new CVSServerException(status);
if ( ! e.isNoTagException() && e.containsErrors())
throw e;
return false;
} else if (status.getSeverity() == IStatus.ERROR && isReportableError(status)) {
throw new CVSException(status);
}
}
for (Iterator iterator = removed.iterator(); iterator.hasNext();) {
ICVSResource resource = (ICVSResource) iterator.next();
if (resource.exists())
resource.delete();
}
} finally {
session.close();
monitor.done();
}
return true;
}
private boolean isReportableError(IStatus status) {
return CVSStatus.isInternalError(status)
|| status.getCode() == TeamException.UNABLE
|| status.getCode() == CVSStatus.INVALID_LOCAL_RESOURCE_PATH
|| status.getCode() == CVSStatus.RESPONSE_HANDLING_FAILURE;
}
private LocalOption[] getLocalOptions() {
ArrayList<LocalOption> options = new ArrayList<>();
if (tag != null)
options.add(Update.makeTagOption(tag));
if (depth != IResource.DEPTH_INFINITE )
options.add(Command.DO_NOT_RECURSE);
if (fetchAbsentDirectories)
options.add(Update.RETRIEVE_ABSENT_DIRECTORIES);
if (!options.isEmpty())
return options.toArray(new LocalOption[options.size()]);
return Command.NO_LOCAL_OPTIONS;
}
public void directoryDoesNotExist(ICVSFolder commandRoot, String path) {
try {
removed.add(commandRoot.getChild(path));
} catch (CVSException e) {
CVSProviderPlugin.log(e);
}
}
public void directoryInformation(ICVSFolder commandRoot, String path,
boolean newDirectory) {
// Nothing to do
}
public void fileDoesNotExist(ICVSFolder parent, String filename) {
try {
removed.add(parent.getChild(filename));
} catch (CVSException e) {
CVSProviderPlugin.log(e);
}
}
public void fileInformation(int type, ICVSFolder parent, String filename) {
// Nothing to do
}
}