blob: 742202a3c449f3fd4315aa0257076b452d5e05cb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.core.retrieve;
import org.apache.ivy.Ivy;
import org.apache.ivy.TestHelper;
import org.apache.ivy.core.IvyPatternHelper;
import org.apache.ivy.core.cache.ResolutionCacheManager;
import org.apache.ivy.core.event.IvyEvent;
import org.apache.ivy.core.event.IvyListener;
import org.apache.ivy.core.event.retrieve.EndRetrieveArtifactEvent;
import org.apache.ivy.core.event.retrieve.EndRetrieveEvent;
import org.apache.ivy.core.event.retrieve.StartRetrieveArtifactEvent;
import org.apache.ivy.core.event.retrieve.StartRetrieveEvent;
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.report.ArtifactDownloadReport;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.util.DefaultMessageLogger;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.MockMessageLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.taskdefs.Delete;
import org.apache.tools.ant.taskdefs.condition.JavaVersion;
import org.apache.tools.ant.types.FilterChain;
import org.apache.tools.ant.filters.TokenFilter.ReplaceString;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class RetrieveTest {
private Ivy ivy;
private static boolean systemHasSymlinkAbility;
@BeforeClass
public static void beforeClass() {
final List<File> tmpFilesCreated = new ArrayList<>();
// create a dummy symlink and see if it works fine
try {
final File tmpDir = Files.createTempDirectory(null).toFile();
tmpFilesCreated.add(tmpDir);
final Path tmpFile = Files.createTempFile(tmpDir.toPath(), null, null);
tmpFilesCreated.add(tmpFile.toFile());
final File symlinkedFile = new File(tmpDir, "symlinked-test-file");
tmpFilesCreated.add(symlinkedFile);
// attempt to create the symlink
Files.createSymbolicLink(symlinkedFile.toPath(), tmpFile);
systemHasSymlinkAbility = true;
} catch (IOException ioe) {
Message.info("Current system is considered as not having symlink ability due to failure to create a test symlink", ioe);
systemHasSymlinkAbility = false;
}
// delete on exit, the tmp files we created
for (final File file : tmpFilesCreated) {
file.deleteOnExit();
}
}
@Before
public void setUp() throws Exception {
ivy = Ivy.newInstance();
ivy.configure(new File("test/repositories/ivysettings.xml"));
TestHelper.createCache();
Message.setDefaultLogger(new DefaultMessageLogger(Message.MSG_INFO));
}
@After
public void tearDown() {
TestHelper.cleanCache();
Delete del = new Delete();
del.setProject(new Project());
del.setDir(new File("build/test/retrieve"));
del.execute();
}
@Test
public void testRetrieveSimple() throws Exception {
// mod1.1 depends on mod1.2
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
assertTrue(new File(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2",
"jar", "jar", "default")).exists());
pattern = "build/test/retrieve/[module]/[conf]/[type]s/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
assertTrue(new File(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2",
"jar", "jar", "default")).exists());
}
@Test
public void wontRetrieveOutsideOfDestRoot() throws Exception {
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
Ivy testIvy = Ivy.newInstance();
testIvy.configure(new File("test/repositories/ivysettings.xml"));
testIvy.getSettings()
.setResolutionCacheManager(new FindAllResolutionCacheManager(ivy.getResolutionCacheManager(), md));
Copy copy = new Copy();
copy.setProject(new Project());
copy.setFile(new File("build/cache/org1-mod1.1-default.xml"));
copy.setTofile(new File("build/cache/fake-default.xml"));
FilterChain fc = copy.createFilterChain();
ReplaceString rsOrg = new ReplaceString();
rsOrg.setFrom("organisation=\"org1\"");
rsOrg.setTo("organisation=\"fake\"");
fc.addReplaceString(rsOrg);
copy.setOverwrite(true);
copy.execute();
MockMessageLogger mockLogger = new MockMessageLogger();
Message.setDefaultLogger(mockLogger);
ModuleRevisionId id = ModuleRevisionId.newInstance("fake", "a", "1.1");
ModuleDescriptor fake = DefaultModuleDescriptor.newDefaultInstance(id);
String pattern = "build/[organisation]/../../../[artifact]-[revision].[ext]";
try {
testIvy.retrieve(fake.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
fail("expected an exception");
} catch (RuntimeException ex) {
assertTrue(ex.getCause() instanceof IllegalArgumentException);
}
}
@Test
public void testRetrieveSameFileConflict() throws Exception {
// mod1.1 depends on mod1.2
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.4/ivys/ivy-1.0.1.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[module]/[artifact]-[revision].[ext]";
MockMessageLogger mockLogger = new MockMessageLogger();
Message.setDefaultLogger(mockLogger);
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
assertTrue(new File(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.2", "mod1.2",
"jar", "jar", "default")).exists());
mockLogger.assertLogDoesntContain("conflict on");
}
@Test(expected = RuntimeException.class)
public void testRetrieveDifferentArtifactsOfSameModuleToSameFile() throws Exception {
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org2/mod2.2/ivys/ivy-0.5.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[module]/[module].[ext]";
MockMessageLogger mockLogger = new MockMessageLogger();
Message.setDefaultLogger(mockLogger);
try {
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
} finally {
mockLogger.assertLogDoesntContain("multiple artifacts");
}
}
@Test
public void testEvent() throws Exception {
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
final List<IvyEvent> events = new ArrayList<>();
ivy.getEventManager().addIvyListener(new IvyListener() {
public void progress(IvyEvent event) {
events.add(event);
}
});
ModuleDescriptor md = report.getModuleDescriptor();
String pattern = "build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
assertEquals(4, events.size());
assertTrue(events.get(0) instanceof StartRetrieveEvent);
assertTrue(events.get(1) instanceof StartRetrieveArtifactEvent);
assertTrue(events.get(2) instanceof EndRetrieveArtifactEvent);
assertTrue(events.get(3) instanceof EndRetrieveEvent);
EndRetrieveEvent ev = (EndRetrieveEvent) events.get(3);
assertEquals(1, ev.getNbCopied());
assertEquals(0, ev.getNbUpToDate());
events.clear();
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
assertEquals(2, events.size());
assertTrue(events.get(0) instanceof StartRetrieveEvent);
assertTrue(events.get(1) instanceof EndRetrieveEvent);
ev = (EndRetrieveEvent) events.get(1);
assertEquals(0, ev.getNbCopied());
assertEquals(1, ev.getNbUpToDate());
}
@Test
public void testRetrieveOverwrite() throws Exception {
// mod1.1 depends on mod1.2
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]";
// we create a fake old file to see if it is overwritten
File file = new File(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0",
"mod1.2", "jar", "jar", "default")).getCanonicalFile();
file.getParentFile().mkdirs();
file.createNewFile();
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setOverwriteMode("always").setDestArtifactPattern(pattern));
assertEquals(new File("test/repositories/1/org1/mod1.2/jars/mod1.2-2.0.jar").lastModified(),
file.lastModified());
}
@Test
public void testRetrieveWithSymlinks() throws Exception {
// mod1.1 depends on mod1.2
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setMakeSymlinks(true).setDestArtifactPattern(pattern));
assertLinkOrExists(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2", "jar",
"jar", "default"));
pattern = "build/test/retrieve/[module]/[conf]/[type]s/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setMakeSymlinks(true).setDestArtifactPattern(pattern));
assertLinkOrExists(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2", "jar",
"jar", "default"));
}
/**
* Tests that retrieve, when invoked with "symlink" enabled, creates the necessary symlink
* when the artifact being retrieved is a directory instead of a regular file
*
* @throws Exception
* @see <a href="https://issues.apache.org/jira/browse/IVY-1594">IVY-1594</a>
*/
@Test
public void testRetrieveZipArtifactWithSymlinks() throws Exception {
// resolve (inline) with org1:mod1.1:3.0 as a dependency
final ResolveReport report = ivy.resolve(new ModuleRevisionId(new ModuleId("org1", "mod1.7"), "3.0"),
getResolveOptions(new String[]{"*"}), false);
assertNotNull("Resolution report is null", report);
final ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull("Module descriptor is null", md);
final String retrievePattern = "build/test/retrieve/[module]/[conf]/[artifact]-[revision]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setMakeSymlinks(true).setDestArtifactPattern(retrievePattern));
final String expectedRetrieveLocation = IvyPatternHelper.substitute(retrievePattern, "org1", "mod1.7",
"3.0", "zipped-artifact", null, null, "default");
// make sure it's retrieved as a symlink (on systems that support symlink)
assertLinkOrExists(expectedRetrieveLocation);
}
/**
* This test is here to just test the deprecated {@code symlinkmass} option for retrieve task.
* A version or two down the line, after 2.5 release, we can remove this test and the option
* altogether.
*
* @throws Exception if something goes wrong
*/
@SuppressWarnings("deprecation")
@Test
public void testRetrieveWithSymlinksMass() throws Exception {
// mod1.1 depends on mod1.2
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setMakeSymlinksInMass(true).setDestArtifactPattern(pattern));
assertLinkOrExists(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2", "jar",
"jar", "default"));
pattern = "build/test/retrieve/[module]/[conf]/[type]s/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setMakeSymlinksInMass(true).setDestArtifactPattern(pattern));
assertLinkOrExists(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2", "jar",
"jar", "default"));
}
/**
* If the system {@link #systemHasSymlinkAbility has symlink ability} then asserts that the
* passed {@code filePath} is a {@link Files#isSymbolicLink(Path) symbolic link}. Else asserts
* that the {@code filePath} {@link Files#exists(Path, LinkOption...) exists}.
*
* @param filePath String
*/
private void assertLinkOrExists(final String filePath) {
if (systemHasSymlinkAbility) {
assertTrue(filePath + " was expected to be a symlink", Files.isSymbolicLink(Paths.get(filePath)));
return;
}
Message.info("System doesn't have symlink ability so checking if path " + filePath + " exists instead of checking for it to be a symlink");
assertTrue("Missing " + filePath, Files.exists(Paths.get(filePath)));
}
@Test
public void testRetrieveWithVariable() throws Exception {
// mod1.1 depends on mod1.2
ivy.setVariable("retrieve.dir", "retrieve");
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org1/mod1.1/ivys/ivy-1.0.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/${retrieve.dir}/[module]/[conf]/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
pattern = IvyPatternHelper.substituteVariable(pattern, "retrieve.dir", "retrieve");
assertTrue(new File(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2",
"jar", "jar", "default")).exists());
pattern = "build/test/${retrieve.dir}/[module]/[conf]/[type]s/[artifact]-[revision].[ext]";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
pattern = IvyPatternHelper.substituteVariable(pattern, "retrieve.dir", "retrieve");
assertTrue(new File(IvyPatternHelper.substitute(pattern, "org1", "mod1.2", "2.0", "mod1.2",
"jar", "jar", "default")).exists());
}
@Test
public void testRetrieveReport() throws Exception {
// mod1.1 depends on mod1.2
ResolveReport report = ivy.resolve(new File(
"test/repositories/1/org20/mod20.1/ivys/ivy-1.2.xml").toURI().toURL(),
getResolveOptions(new String[] {"*"}));
assertNotNull(report);
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
ModuleRevisionId mrid = md.getModuleRevisionId();
RetrieveOptions options = getRetrieveOptions();
options.setConfs(new String[] {"A"});
Map<ArtifactDownloadReport, Set<String>> artifactsToCopy = ivy.getRetrieveEngine()
.determineArtifactsToCopy(mrid,
"build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]", options);
assertEquals(2, artifactsToCopy.size());
options.setConfs(new String[] {"B"});
artifactsToCopy = ivy.getRetrieveEngine().determineArtifactsToCopy(mrid,
"build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]", options);
assertEquals(2, artifactsToCopy.size());
options.setConfs(new String[] {"A", "B"});
artifactsToCopy = ivy.getRetrieveEngine().determineArtifactsToCopy(mrid,
"build/test/retrieve/[module]/[conf]/[artifact]-[revision].[ext]", options);
assertEquals(3, artifactsToCopy.size());
}
@Test
public void testUnpack() throws Exception {
ResolveOptions roptions = getResolveOptions(new String[] {"*"});
URL url = new File("test/repositories/1/packaging/module1/ivys/ivy-1.0.xml").toURI()
.toURL();
// normal resolve, the file goes in the cache
ResolveReport report = ivy.resolve(url, roptions);
assertFalse(report.hasError());
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[organization]/[module]/[conf]/[type]s/[artifact]-[revision](.[ext])";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
File dest = new File("build/test/retrieve/packaging/module2/default/jars/module2-1.0");
assertTrue(dest.exists());
assertTrue(dest.isDirectory());
File[] jarContents = dest.listFiles();
Arrays.sort(jarContents);
assertEquals(new File(dest, "META-INF"), jarContents[0]);
assertEquals(new File(dest, "test.txt"), jarContents[1]);
assertEquals(new File(dest, "META-INF/MANIFEST.MF"), jarContents[0].listFiles()[0]);
}
@Test
public void testUnpackSync() throws Exception {
ResolveOptions roptions = getResolveOptions(new String[] {"*"});
URL url = new File("test/repositories/1/packaging/module1/ivys/ivy-1.0.xml").toURI()
.toURL();
// normal resolve, the file goes in the cache
ResolveReport report = ivy.resolve(url, roptions);
assertFalse(report.hasError());
ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull(md);
String pattern = "build/test/retrieve/[organization]/[module]/[conf]/[type]s/[artifact]-[revision](.[ext])";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setSync(true).setDestArtifactPattern(pattern));
File dest = new File("build/test/retrieve/packaging/module2/default/jars/module2-1.0");
assertTrue(dest.exists());
assertTrue(dest.isDirectory());
File[] jarContents = dest.listFiles();
Arrays.sort(jarContents);
assertEquals(new File(dest, "META-INF"), jarContents[0]);
assertEquals(new File(dest, "test.txt"), jarContents[1]);
assertEquals(new File(dest, "META-INF/MANIFEST.MF"), jarContents[0].listFiles()[0]);
}
/**
* Test case for IVY-1478.
* {@link RetrieveEngine} must retrieve artifacts with the correct extension if the artifact
* is unpacked.
*
* @throws Exception if something goes wrong
* @see <a href="https://issues.apache.org/jira/browse/IVY-1478">IVY-1478</a>
*/
@Test
public void testUnpackExt() throws Exception {
final JavaVersion java14OrHigher = new JavaVersion();
java14OrHigher.setAtLeast("14");
Assume.assumeFalse("Pack200 tools and API have been removed since JDK 14", java14OrHigher.eval());
final ResolveOptions roptions = getResolveOptions(new String[] {"*"});
final URL url = new File("test/repositories/1/packaging/module10/ivys/ivy-1.0.xml").toURI()
.toURL();
// normal resolve, the file goes in the cache
final ResolveReport report = ivy.resolve(url, roptions);
assertFalse("Resolution report has errors", report.hasError());
final ModuleDescriptor md = report.getModuleDescriptor();
assertNotNull("Module descriptor from report was null", md);
final String pattern = "build/test/retrieve/[organization]/[module]/[conf]/[type]s/[artifact]-[revision](.[ext])";
ivy.retrieve(md.getModuleRevisionId(),
getRetrieveOptions().setDestArtifactPattern(pattern));
final File dest = new File("build/test/retrieve/packaging/module9/default/jars/module9-1.0.jar");
assertTrue("Retrieved artifact is missing at " + dest.getAbsolutePath(), dest.exists());
assertTrue("Retrieved artifact at " + dest.getAbsolutePath() + " is not a file", dest.isFile());
}
/**
* Test case for IVY-1498.
* More than one retrieve, one having symlink enabled and the other not,
* must not lead to repository cache corruption.
* <p>
* The test does the following:
* </p>
* <ul>
* <li>
* Resolves a module, let's call it "irrelevant-A" which has a dependency on
* "org:foo-bar:1.2.3"
* </li>
* <li>
* Invokes a retrieve RT1, with {@link RetrieveOptions#setMakeSymlinks(boolean) symlinks true}
* on that resolved module with a pattern that translates to a path "a/b/c/foo-bar.jar". When
* the RT1 retrieve is done, the path "a/b/c/foo-bar.jar" will be a symlink to a path
* "repo/cache/org/foo-bar/foo-bar-1.2.3.jar" in the Ivy cache.
* All fine so far.
* </li>
* <li>
* We then resolve another module, let's call it "irrelevant-B" which has a dependency on
* "org:foo-bar:2.3.4"
* </li>
* <li>
* Next, do a new retrieve RT2, on this newly resolved module with
* {@link RetrieveOptions#setMakeSymlinks(boolean) symlinks false} and
* {@link RetrieveOptions#getOverwriteMode() overwrite semantics enabled}
* and with the same pattern as before), that translates to a path "a/b/c/foo-bar.jar".
* </li>
* </ul>
* <p>
* When RT2 retrieve is done, we expect the path "a/b/c/foo-bar.jar" will *not* be a symlink
* and instead be an actual file that represents the org:foo-bar:2.3.4 artifact jar.
* </p>
* <p>
* We also expect that this RT2 retrieve will not update/overwrite the file at path
* "repo/cache/org/foo-bar/foo-bar-1.2.3.jar" in the Ivy cache - the file that was the end
* target of the symlink generated by the previous RT1.
* </p>
*
* @throws Exception if something goes wrong
* @see <a href="https://issues.apache.org/jira/browse/IVY-1498">IVY-1498</a>
*/
@Test
public void testRetrieveWithSymlinkToggling() throws Exception {
// org:mod1:2.0 depends on org:foo-bar:1.2.3
final ResolveReport resolve1Report = ivy.resolve(new File("test/repositories/1/org/mod1/ivys/ivy-2.0.xml").toURI().toURL(),
getResolveOptions(new String[]{"*"}));
assertNotNull("Resolve report isn't available", resolve1Report);
assertNotNull("Module descriptor missing in resolve report", resolve1Report.getModuleDescriptor());
// verify the downloaded dependency artifact in cache
final ModuleRevisionId fooBar123Mrid = ModuleRevisionId.newInstance("org", "foo-bar", "1.2.3");
final ArtifactDownloadReport[] downloadReports = resolve1Report.getArtifactsReports(fooBar123Mrid);
assertNotNull("No artifact download report found for the " + fooBar123Mrid + " dependency", downloadReports);
assertEquals("Unexpected number for artifact download reports for the " + fooBar123Mrid + " dependency", 1, downloadReports.length);
final File fooBar123ArtifactInCache = downloadReports[0].getLocalFile();
assertNotNull("Local file for " + fooBar123Mrid + " missing in download report", fooBar123ArtifactInCache);
assertTrue("Artifact file for " + fooBar123Mrid + " isn't a file, in cache at " + fooBar123ArtifactInCache,
fooBar123ArtifactInCache.isFile());
final byte[] fooBar123ArtifactContentsInCache = Files.readAllBytes(fooBar123ArtifactInCache.toPath());
assertTrue("Artifact content was empty at " + new String(fooBar123ArtifactContentsInCache), fooBar123ArtifactContentsInCache.length > 0);
// now do a retrieve of the resolved module
final String retrievePattern = "build/test/retrieve/symlink-test/[module]/[artifact].[ext]";
ivy.retrieve(resolve1Report.getModuleDescriptor().getModuleRevisionId(),
getRetrieveOptions().setMakeSymlinks(true).setOverwriteMode(RetrieveOptions.OVERWRITEMODE_ALWAYS)
.setDestArtifactPattern(retrievePattern));
// we expect org:foo-bar:1.2.3 to have been retrieved
final String retrievedArtifactSymlinkPath = IvyPatternHelper.substitute(retrievePattern, "org", "foo-bar",
"1.2.3", "foo-bar", "jar", "jar", "default");
assertLinkOrExists(retrievedArtifactSymlinkPath);
// get hold of the contents of the retrieved artifact
final byte[] retrievedArtifactContent = Files.readAllBytes(Paths.get(retrievedArtifactSymlinkPath));
// compare it to the contents of org:foo-bar:1.2.3 artifact in repo cache. Should be the same
assertTrue("Unexpected content in the retrieved artifact at " + retrievedArtifactSymlinkPath,
Arrays.equals(fooBar123ArtifactContentsInCache, retrievedArtifactContent));
// let this retrieved artifact file stay and let's now try and resolve a module and then
// retrieve a different version of the same artifact, *without* symlinking enabled, which
// will end up at this exact location org:mod1:2.0 depends on org:foo-bar:1.2.3
final ResolveReport resolve2Report = ivy.resolve(new File("test/repositories/1/org/mod1/ivys/ivy-5.0.xml").toURI().toURL(),
getResolveOptions(new String[]{"*"}));
assertNotNull("Resolve report isn't available", resolve2Report);
assertNotNull("Module descriptor missing in resolve report", resolve2Report.getModuleDescriptor());
// verify the downloaded dependency artifact in cache
final ModuleRevisionId fooBar234Mrid = ModuleRevisionId.newInstance("org", "foo-bar", "2.3.4");
final ArtifactDownloadReport[] foobar234report = resolve2Report.getArtifactsReports(fooBar234Mrid);
assertNotNull("No artifact download report found for the " + fooBar234Mrid + " dependency", foobar234report);
assertEquals("Unexpected number for artifact download reports for the " + fooBar234Mrid + " dependency", 1, foobar234report.length);
final File foobar234InCache = foobar234report[0].getLocalFile();
assertNotNull("Local file for " + fooBar234Mrid + " missing in download report", foobar234InCache);
assertTrue("Artifact file for " + fooBar234Mrid + " isn't a file, in cache at " + foobar234InCache,
foobar234InCache.isFile());
final byte[] foobar234CacheFileContents = Files.readAllBytes(foobar234InCache.toPath());
assertTrue("Artifact content was empty at " + new String(foobar234CacheFileContents), foobar234CacheFileContents.length > 0);
// do the retrieve with symlinks disabled
ivy.retrieve(resolve2Report.getModuleDescriptor().getModuleRevisionId(),
getRetrieveOptions().setMakeSymlinks(false).setDestArtifactPattern(retrievePattern)
.setOverwriteMode(RetrieveOptions.OVERWRITEMODE_ALWAYS));
// we expect org:foo-bar:2.3.4 to have been retrieved
final Path secondRetrieveArtifactPath = new File(IvyPatternHelper.substitute(retrievePattern, "org", "foo-bar",
"2.3.4", "foo-bar", "jar", "jar", "default")).toPath();
assertTrue("Artifact wasn't retrieved to " + secondRetrieveArtifactPath, Files.exists(secondRetrieveArtifactPath));
// expected to be a regular file and not a symlink
assertFalse("Artifact retrieved at " + secondRetrieveArtifactPath + " wasn't expected to be a "
+ "symlink", Files.isSymbolicLink(secondRetrieveArtifactPath));
// get hold of the contents of the retrieved artifact
final byte[] secondRetrievedArtifactContents = Files.readAllBytes(secondRetrieveArtifactPath);
// compare it to the contents of org:foo-bar:2.3.4 artifact in repo cache. Should be the same
assertTrue("Unexpected content in the retrieved artifact at " + secondRetrieveArtifactPath,
Arrays.equals(foobar234CacheFileContents, secondRetrievedArtifactContents));
// also make sure that this retrieved content hasn't messed up the other (unrelated) file,
// in the cache, that was previously symlinked to this retrieved path
assertTrue("A second retrieve of an artifact has corrupted an unrelated (previously symlinked) artifact in cache",
Arrays.equals(fooBar123ArtifactContentsInCache, Files.readAllBytes(Paths.get(fooBar123ArtifactInCache.getPath()))));
}
private RetrieveOptions getRetrieveOptions() {
return new RetrieveOptions();
}
private ResolveOptions getResolveOptions(final String[] confs) {
return new ResolveOptions().setConfs(confs);
}
private static class FindAllResolutionCacheManager implements ResolutionCacheManager {
private final ResolutionCacheManager real;
private final ModuleRevisionId staticMrid;
private final ModuleDescriptor staticModuleDescriptor;
private static final String RESOLVE_ID = "org1-mod1.1";
private FindAllResolutionCacheManager(ResolutionCacheManager real, ModuleDescriptor md) {
this.real = real;
staticModuleDescriptor = md;
staticMrid = md.getModuleRevisionId();
}
public File getResolutionCacheRoot() {
return real.getResolutionCacheRoot();
}
public File getResolvedIvyFileInCache(ModuleRevisionId mrid) {
return real.getResolvedIvyFileInCache(staticMrid);
}
public File getResolvedIvyPropertiesInCache(ModuleRevisionId mrid) {
return real.getResolvedIvyPropertiesInCache(staticMrid);
}
public File getConfigurationResolveReportInCache(String resolveId, String conf) {
return new File("build/cache/fake-default.xml");
}
public File[] getConfigurationResolveReportsInCache(final String resolveId) {
return real.getConfigurationResolveReportsInCache(RESOLVE_ID);
}
public ModuleDescriptor getResolvedModuleDescriptor(ModuleRevisionId mrid)
throws ParseException, IOException {
if (mrid.getOrganisation().equals("fake")) {
DefaultModuleDescriptor md = new DefaultModuleDescriptor(staticModuleDescriptor.getParser(), staticModuleDescriptor.getResource());
md.setModuleRevisionId(mrid);
md.setPublicationDate(staticModuleDescriptor.getPublicationDate());
for (Configuration conf : staticModuleDescriptor.getConfigurations()) {
md.addConfiguration(conf);
}
return md;
}
return real.getResolvedModuleDescriptor(mrid);
}
public void saveResolvedModuleDescriptor(ModuleDescriptor md) {
}
public void clean() {
}
}
}