blob: 7a99f31a13dec13911f34afb3e3fd5691dbf3878 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2009 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
*******************************************************************************/
package org.eclipse.equinox.p2.tests.artifact.repository;
import org.eclipse.equinox.p2.core.ProvisionException;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.artifact.repository.MirrorRequest;
import org.eclipse.equinox.internal.p2.artifact.repository.MirrorSelector;
import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository;
import org.eclipse.equinox.internal.p2.metadata.ArtifactKey;
import org.eclipse.equinox.internal.provisional.p2.metadata.Version;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.IQueryable;
import org.eclipse.equinox.internal.provisional.spi.p2.artifact.repository.AbstractArtifactRepository;
import org.eclipse.equinox.internal.provisional.spi.p2.repository.AbstractRepository;
import org.eclipse.equinox.p2.metadata.IArtifactKey;
import org.eclipse.equinox.p2.metadata.query.IQuery;
import org.eclipse.equinox.p2.metadata.query.IQueryResult;
import org.eclipse.equinox.p2.repository.IRepository;
import org.eclipse.equinox.p2.repository.artifact.*;
import org.eclipse.equinox.p2.tests.AbstractProvisioningTest;
import org.eclipse.equinox.p2.tests.AbstractWrappedArtifactRepository;
import org.w3c.dom.*;
public class MirrorRequestTest extends AbstractProvisioningTest {
private static final String testDataLocation = "testData/artifactRepo/emptyJarRepo";
File targetLocation;
IArtifactRepository targetRepository, sourceRepository;
URI destination, failedOptimized, pakedRepositoryLocation;
public void setUp() throws Exception {
super.setUp();
targetLocation = File.createTempFile("target", ".repo");
targetLocation.delete();
targetLocation.mkdirs();
targetRepository = new SimpleArtifactRepository("TargetRepo", targetLocation.toURI(), null);
IArtifactRepositoryManager mgr = getArtifactRepositoryManager();
sourceRepository = mgr.loadRepository((getTestData("EmptyJar repo", testDataLocation).toURI()), null);
failedOptimized = URIUtil.toJarURI(getTestData("Error loading test data", "testData/mirror/invalidPackedMissingCanonical.zip").toURI(), null);
pakedRepositoryLocation = getTestData("Error loading packed repository", "testData/mirror/mirrorPackedRepo").toURI();
destination = getTempFolder().toURI();
}
protected void tearDown() throws Exception {
getArtifactRepositoryManager().removeRepository(destination);
getArtifactRepositoryManager().removeRepository(failedOptimized);
getArtifactRepositoryManager().removeRepository(targetLocation.toURI());
getArtifactRepositoryManager().removeRepository(pakedRepositoryLocation);
AbstractProvisioningTest.delete(targetLocation);
delete(new File(destination));
super.tearDown();
}
public void testInvalidZipFileInTheSource() {
IArtifactKey key = new ArtifactKey("org.eclipse.update.feature", "HelloWorldFeature", Version.createOSGi(1, 0, 0));
Map<String, String> targetProperties = new HashMap<String, String>();
targetProperties.put("artifact.folder", "true");
MirrorRequest request = new MirrorRequest(key, targetRepository, null, targetProperties);
request.setSourceRepository(sourceRepository);
request.perform(new NullProgressMonitor());
assertTrue(request.getResult().matches(IStatus.ERROR));
assertTrue(request.getResult().getException() instanceof IOException);
}
public void testMissingArtifact() {
IArtifactKey key = new ArtifactKey("org.eclipse.update.feature", "Missing", Version.createOSGi(1, 0, 0));
Map<String, String> targetProperties = new HashMap<String, String>();
targetProperties.put("artifact.folder", "true");
MirrorRequest request = new MirrorRequest(key, targetRepository, null, targetProperties);
request.setSourceRepository(sourceRepository);
request.perform(new NullProgressMonitor());
assertTrue(request.getResult().matches(IStatus.ERROR));
}
// Test that if MirrorRequest fails to download a packed artifact it attempts the canonical version
public void testFailToCanonical() {
RemoteRepo src = new RemoteRepo((SimpleArtifactRepository) sourceRepository);
IArtifactKey key = new ArtifactKey("test.txt", "fail_to_canonical", Version.parseVersion("1.0.0"));
MirrorRequest request = new MirrorRequest(key, targetRepository, null, null);
request.setSourceRepository(src);
request.perform(new NullProgressMonitor());
assertTrue(request.getResult().toString(), request.getResult().isOK());
assertTrue("Target does not contain artifact.", targetRepository.contains(key));
assertTrue("Number of downloads differs from expected attempts.", src.downloadCount == 2);
}
// Test that SimpleArtifactRepository & MirrorRequest use mirrors in the event of a failure.
public void testMirrorFailOver() {
OrderedMirrorSelector selector = new OrderedMirrorSelector(sourceRepository);
// call test
IArtifactKey key = new ArtifactKey("test.txt", "HelloWorldText", Version.parseVersion("1.0.0"));
MirrorRequest request = new MirrorRequest(key, targetRepository, null, null);
request.setSourceRepository(sourceRepository);
request.perform(new NullProgressMonitor());
// The download succeeded
assertTrue(request.getResult().toString(), request.getResult().isOK());
// All available mirrors used
assertEquals("All mirrors utilized", selector.mirrors.length, selector.index);
}
/*
* Test that the expected Status level is returned when a mirror fails from packed to canonical
*/
public void testStatusFromFailover() {
StatusSequenceRepository source = null;
LinkedList seq = new LinkedList();
try {
source = new StatusSequenceRepository(getArtifactRepositoryManager().loadRepository(pakedRepositoryLocation, new NullProgressMonitor()));
} catch (ProvisionException e) {
fail("Failed to load source repository");
}
// Set status sequence, actual Statuses added later
source.setSequence(seq);
// Grab an ArtifactKey to mirror, doesn't matter which
IQueryResult keys = source.query(ArtifactKeyQuery.ALL_KEYS, null);
assertTrue("Unable to obtain artifact keys", keys != null && !keys.isEmpty());
IArtifactKey key = (IArtifactKey) keys.iterator().next();
MirrorRequest req = new MirrorRequest(key, targetRepository, null, null);
req.setSourceRepository(source);
// Set Status sequence
seq.add(new Status(IStatus.ERROR, "Activator", "Message"));
seq.add(new Status(IStatus.WARNING, "Activator", "Message"));
req.perform(new NullProgressMonitor());
assertEquals("Expected WARNING status", IStatus.WARNING, req.getResult().getSeverity());
// Remove key from repo so the same one can be used
targetRepository.removeDescriptor(key);
// Set Status sequence
req = new MirrorRequest(key, targetRepository, null, null);
req.setSourceRepository(source);
seq.add(new Status(IStatus.WARNING, "Activator", "Message"));
seq.add(new Status(IStatus.INFO, "Activator", "Message"));
req.perform(new NullProgressMonitor());
assertEquals("Expected INFO status", IStatus.INFO, req.getResult().getSeverity());
// Remove key from repo so the same one can be used
targetRepository.removeDescriptor(key);
// Set Status sequence
req = new MirrorRequest(key, targetRepository, null, null);
req.setSourceRepository(source);
seq.add(new Status(IStatus.INFO, "Activator", "Message"));
req.perform(new NullProgressMonitor());
assertEquals("Expected OK status", IStatus.OK, req.getResult().getSeverity());
}
/*
*
*/
public void testFailedOptimizedMissingCanonical() {
try {
IArtifactRepository source = new AbstractWrappedArtifactRepository(getArtifactRepositoryManager().loadRepository(failedOptimized, new NullProgressMonitor())) {
public URI getLocation() {
try {
return new URI("http://nowhere");
} catch (URISyntaxException e) {
fail("Failed to create URI", e);
return null;
}
}
};
IArtifactRepository target = getArtifactRepositoryManager().createRepository(destination, "Destination", IArtifactRepositoryManager.TYPE_SIMPLE_REPOSITORY, null);
IArtifactKey key = new ArtifactKey("osgi.bundle", "org.eclipse.ve.jfc", Version.parseVersion("1.4.0.HEAD"));
MirrorRequest req = new MirrorRequest(key, target, null, null);
req.setSourceRepository(source);
req.perform(new NullProgressMonitor());
IStatus result = req.getResult();
assertTrue("MirrorRequest should have failed", result.matches(IStatus.ERROR));
assertEquals("Result should contain two failures", 2, result.getChildren().length);
assertStatusContains("Return status does not contain Signature Verification failure", result, "Invalid content:");
assertStatusContains("Return status does not contain Missing Artifact status", result, "Artifact not found:");
} catch (ProvisionException e) {
fail("Failed to load repositories", e);
}
}
protected static void assertStatusContains(String message, IStatus status, String statusString) {
if (!statusContains(status, statusString))
fail(message);
}
class StatusSequenceRepository extends AbstractWrappedArtifactRepository {
Queue sequence;
public StatusSequenceRepository(IArtifactRepository repo) {
super(repo);
}
public URI getLocation() {
// Lie about the location so packed files are used
try {
return new URI("http://somewhere");
} catch (URISyntaxException e) {
return null;
}
}
public IStatus getArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
try {
destination.write(new byte[] {1, 1, 2});
} catch (Exception e) {
fail("Failed to write to stream", e);
}
if (sequence.isEmpty())
return Status.OK_STATUS;
return (IStatus) sequence.remove();
}
public void setSequence(Queue queue) {
sequence = queue;
}
}
private static boolean statusContains(IStatus status, String statusString) {
if (status.getMessage().indexOf(statusString) != -1)
return true;
if (!status.isMultiStatus())
return false;
IStatus[] children = status.getChildren();
for (int i = 0; i < children.length; i++)
if (statusContains(children[i], statusString))
return true;
return false;
}
// Repository which misleads about its location
protected class RemoteRepo extends AbstractArtifactRepository {
SimpleArtifactRepository delegate;
int downloadCount = 0;
RemoteRepo(SimpleArtifactRepository repo) {
super(repo.getName(), repo.getType(), repo.getVersion(), repo.getLocation(), repo.getDescription(), repo.getProvider(), repo.getProperties());
delegate = repo;
}
public synchronized URI getLocation() {
try {
return new URI("http://test/");
} catch (URISyntaxException e) {
// Should never happen, but we'll fail anyway
fail("URI creation failed", e);
return null;
}
}
public boolean contains(IArtifactDescriptor descriptor) {
return delegate.contains(descriptor);
}
public boolean contains(IArtifactKey key) {
return delegate.contains(key);
}
public IStatus getArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
downloadCount++;
return delegate.getArtifact(descriptor, destination, monitor);
}
public IArtifactDescriptor[] getArtifactDescriptors(IArtifactKey key) {
return delegate.getArtifactDescriptors(key);
}
public IStatus getArtifacts(IArtifactRequest[] requests, IProgressMonitor monitor) {
return delegate.getArtifacts(requests, monitor);
}
public OutputStream getOutputStream(IArtifactDescriptor descriptor) throws ProvisionException {
return delegate.getOutputStream(descriptor);
}
public IStatus getRawArtifact(IArtifactDescriptor descriptor, OutputStream destination, IProgressMonitor monitor) {
return delegate.getRawArtifact(descriptor, destination, monitor);
}
public IQueryable<IArtifactDescriptor> descriptorQueryable() {
return delegate.descriptorQueryable();
}
public IQueryResult<IArtifactKey> query(IQuery<IArtifactKey> query, IProgressMonitor monitor) {
return delegate.query(query, monitor);
}
}
/*
* Special mirror selector for testing which chooses mirrors in order
*/
protected class OrderedMirrorSelector extends MirrorSelector {
private URI repoLocation;
int index = 0;
MirrorInfo[] mirrors;
IArtifactRepository repo;
OrderedMirrorSelector(IArtifactRepository repo) {
super(repo);
this.repo = repo;
// Setting this property forces SimpleArtifactRepository to use mirrors despite being a local repo
// Alternatively we could use reflect to change "location" of the repo
repo.setProperty(SimpleArtifactRepository.PROP_FORCE_THREADING, String.valueOf(true));
setSelector();
getRepoLocation();
mirrors = computeMirrors("file:///" + getTestData("Mirror Location", testDataLocation + '/' + repo.getProperties().get(IRepository.PROP_MIRRORS_URL)).toString().replace('\\', '/'));
}
// Hijack the source repository's MirrorSelector
private void setSelector() {
Field mirrorField = null;
try {
mirrorField = SimpleArtifactRepository.class.getDeclaredField("mirrors");
mirrorField.setAccessible(true);
mirrorField.set(repo, this);
} catch (Exception e) {
fail("0.2", e);
}
}
// Overridden to prevent mirror sorting
@Override
public synchronized void reportResult(String toDownload, IStatus result) {
return;
}
// We want to test each mirror once.
@Override
public synchronized boolean hasValidMirror() {
return mirrors != null && index < mirrors.length;
}
@Override
public synchronized URI getMirrorLocation(URI inputLocation, IProgressMonitor monitor) {
return URIUtil.append(nextMirror(), repoLocation.relativize(inputLocation).getPath());
}
private URI nextMirror() {
Field mirrorLocation = null;
try {
mirrorLocation = MirrorInfo.class.getDeclaredField("locationString");
mirrorLocation.setAccessible(true);
return URIUtil.makeAbsolute(new URI((String) mirrorLocation.get(mirrors[index++])), repoLocation);
} catch (Exception e) {
fail(Double.toString(0.4 + index), e);
return null;
}
}
private synchronized void getRepoLocation() {
Field locationField = null;
try {
locationField = AbstractRepository.class.getDeclaredField("location");
locationField.setAccessible(true);
repoLocation = (URI) locationField.get(repo);
} catch (Exception e) {
fail("0.3", e);
}
}
private MirrorInfo[] computeMirrors(String mirrorsURL) {
// Copied & modified from MirrorSelector
try {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document document = builder.parse(mirrorsURL);
if (document == null)
return null;
NodeList mirrorNodes = document.getElementsByTagName("mirror"); //$NON-NLS-1$
int mirrorCount = mirrorNodes.getLength();
MirrorInfo[] infos = new MirrorInfo[mirrorCount + 1];
for (int i = 0; i < mirrorCount; i++) {
Element mirrorNode = (Element) mirrorNodes.item(i);
String infoURL = mirrorNode.getAttribute("url"); //$NON-NLS-1$
infos[i] = new MirrorInfo(infoURL, i);
}
//p2: add the base site as the last resort mirror so we can track download speed and failure rate
infos[mirrorCount] = new MirrorInfo(repoLocation.toString(), mirrorCount);
return infos;
} catch (Exception e) {
// log if absolute url
if (mirrorsURL != null && (mirrorsURL.startsWith("http://") //$NON-NLS-1$
|| mirrorsURL.startsWith("https://") //$NON-NLS-1$
|| mirrorsURL.startsWith("file://") //$NON-NLS-1$
|| mirrorsURL.startsWith("ftp://") //$NON-NLS-1$
|| mirrorsURL.startsWith("jar://"))) //$NON-NLS-1$
fail("Error processing mirrors URL: " + mirrorsURL, e); //$NON-NLS-1$
return null;
}
}
}
}