blob: 6f7bf816cd7080cedc76ec2bdc48e06152775a8c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* compeople AG (Stefan Liebig) - various ongoing maintenance
*******************************************************************************/
package org.eclipse.equinox.internal.p2.artifact.repository;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.*;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStepDescriptor;
import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStepHandler;
import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException;
import org.eclipse.equinox.internal.provisional.p2.metadata.IArtifactKey;
import org.eclipse.osgi.util.NLS;
/**
* A request to mirror (copy) an artifact into a given destination artifact repository.
*/
public class MirrorRequest extends ArtifactRequest {
private static final ProcessingStepDescriptor[] EMPTY_STEPS = new ProcessingStepDescriptor[0];
private final IArtifactRepository target;
private final Properties targetDescriptorProperties;
private final Properties targetRepositoryProperties;
public MirrorRequest(IArtifactKey key, IArtifactRepository targetRepository, Properties targetDescriptorProperties, Properties targetRepositoryProperties) {
super(key);
target = targetRepository;
if (targetDescriptorProperties == null || targetDescriptorProperties.isEmpty()) {
this.targetDescriptorProperties = null;
} else {
this.targetDescriptorProperties = new Properties();
this.targetDescriptorProperties.putAll(targetDescriptorProperties);
}
if (targetRepositoryProperties == null || targetRepositoryProperties.isEmpty()) {
this.targetRepositoryProperties = null;
} else {
this.targetRepositoryProperties = new Properties();
this.targetRepositoryProperties.putAll(targetRepositoryProperties);
}
}
public void perform(IProgressMonitor monitor) {
monitor.subTask(NLS.bind(Messages.downloading, getArtifactKey().getId()));
// Do we already have the artifact in the target?
if (target.contains(getArtifactKey())) {
setResult(new Status(IStatus.OK, Activator.ID, NLS.bind(Messages.available_already_in, getArtifactKey())));
return;
}
// if the request does not have a descriptor then try to fill one in by getting
// the list of all and randomly picking one that appears to be optimized.
IArtifactDescriptor optimized = null;
IArtifactDescriptor canonical = null;
if (descriptor == null) {
IArtifactDescriptor[] descriptors = source.getArtifactDescriptors(getArtifactKey());
if (descriptors.length > 0) {
for (int i = 0; i < descriptors.length; i++) {
if (descriptors[i].getProperty(IArtifactDescriptor.FORMAT) == null)
canonical = descriptors[i];
else if (ProcessingStepHandler.canProcess(descriptors[i]))
optimized = descriptors[i];
}
boolean chooseCanonical = source.getLocation().getProtocol().equals("file"); //$NON-NLS-1$
// If the source repo is local then look for a canonical descriptor so we don't waste processing time.
descriptor = chooseCanonical ? canonical : optimized;
// if the descriptor is still null then we could not find our first choice of format so switch the logic.
if (descriptor == null)
descriptor = !chooseCanonical ? canonical : optimized;
}
}
// if the descriptor is not set now then the repo does not have the requested artifact
// TODO improve the reporting here. It may be the case that the repo has the artifact
// but the client does not have a processor
if (descriptor == null) {
setResult(new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.artifact_not_found, getArtifactKey())));
return;
}
ArtifactDescriptor destinationDescriptor = getDestinationDescriptor(descriptor);
IStatus status = transfer(destinationDescriptor, descriptor, monitor);
// if ok, cancelled or transfer has already been done with the canonical form return with status set
if (status.getSeverity() == IStatus.CANCEL) {
setResult(status);
return;
}
if (monitor.isCanceled()) {
setResult(Status.CANCEL_STATUS);
return;
}
if (status.isOK() || descriptor == canonical || canonical == null) {
setResult(status);
return;
}
// retry with canonical, first remove possibly erroneously added descriptor
if (target.contains(destinationDescriptor))
target.removeDescriptor(destinationDescriptor);
setResult(transfer(getDestinationDescriptor(canonical), canonical, monitor));
}
private ArtifactDescriptor getDestinationDescriptor(IArtifactDescriptor sourceDescriptor) {
// Get the descriptor to use to store the artifact
// Since we are mirroring, ensure we clear out data from the original descriptor that may
// not apply in the new repo location.
// TODO this is brittle. perhaps the repo itself should do this? there are cases where
// we really do need to give the repo the actual descriptor to use however...
ArtifactDescriptor destinationDescriptor = new ArtifactDescriptor(sourceDescriptor);
destinationDescriptor.setProcessingSteps(EMPTY_STEPS);
destinationDescriptor.setProperty(IArtifactDescriptor.DOWNLOAD_MD5, null);
destinationDescriptor.setProperty(IArtifactDescriptor.DOWNLOAD_CONTENTTYPE, null);
destinationDescriptor.setProperty(IArtifactDescriptor.FORMAT, null);
if (targetDescriptorProperties != null)
destinationDescriptor.addProperties(targetDescriptorProperties);
if (targetRepositoryProperties != null)
destinationDescriptor.addRepositoryProperties(targetRepositoryProperties);
return destinationDescriptor;
}
/**
* Keep retrying the source repository until it reports back that it will be impossible
* to get the artifact from it.
* @param destinationDescriptor
* @param sourceDescriptor
* @param monitor
* @return the status of the transfer operation
*/
private IStatus transfer(IArtifactDescriptor destinationDescriptor, IArtifactDescriptor sourceDescriptor, IProgressMonitor monitor) {
IStatus status = Status.OK_STATUS;
// go until we get one (OK), there are no more mirrors to consider or the operation is cancelled.
// TODO this needs to be redone with a much better mirror management scheme.
do {
status = transferSingle(destinationDescriptor, sourceDescriptor, monitor);
} while (status.getSeverity() == IStatus.ERROR && status.getCode() == IArtifactRepository.CODE_RETRY);
return status;
}
private IStatus transferSingle(IArtifactDescriptor destinationDescriptor, IArtifactDescriptor sourceDescriptor, IProgressMonitor monitor) {
OutputStream destination;
try {
destination = target.getOutputStream(destinationDescriptor);
} catch (ProvisionException e) {
return e.getStatus();
}
IStatus status = null;
// Do the actual transfer
try {
status = getSourceRepository().getArtifact(sourceDescriptor, destination, monitor);
} finally {
try {
destination.close();
} catch (IOException e) {
if (status != null && status.getSeverity() == IStatus.ERROR && status.getCode() == IArtifactRepository.CODE_RETRY)
status = new MultiStatus(Activator.ID, status.getCode(), new IStatus[] {status}, NLS.bind(Messages.error_closing_stream, getArtifactKey(), target.getLocation()), e);
else
status = new Status(IStatus.ERROR, Activator.ID, NLS.bind(Messages.error_closing_stream, getArtifactKey(), target.getLocation()), e);
}
}
return status;
}
public String toString() {
return Messages.mirroring + getArtifactKey() + " into " + target; //$NON-NLS-1$
}
}