Issue #5264 - Supporting extract of maven archive to destination

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2020-09-18 15:29:02 -05:00
parent e338310c0a
commit ba78355bd6
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
4 changed files with 160 additions and 42 deletions

View File

@ -21,11 +21,15 @@ package org.eclipse.jetty.start;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.Enumeration;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class FS
{
@ -168,4 +172,52 @@ public class FS
{
return path.toRealPath();
}
public static void extract(Path archive, Path destination) throws IOException
{
String filename = archive.getFileName().toString().toLowerCase(Locale.US);
if (filename.endsWith(".jar") || filename.endsWith(".zip"))
{
extractZip(archive, destination);
}
else
{
throw new IOException("Unable to extract unsupported archive format: " + archive);
}
}
public static void extractZip(Path archive, Path destination) throws IOException
{
try (ZipFile zip = new ZipFile(archive.toFile()))
{
StartLog.info("extract %s to %s", archive, destination);
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements())
{
ZipEntry entry = entries.nextElement();
if (entry.isDirectory() || entry.getName().startsWith("/META-INF"))
{
// skip
continue;
}
Path destFile = destination.resolve(entry.getName());
if (!Files.exists(destFile))
{
FS.ensureDirectoryExists(destFile.getParent());
try (InputStream input = zip.getInputStream(entry))
{
StartLog.debug("extracting %s", destFile);
Files.copy(input, destFile);
}
}
else
{
StartLog.debug("skipping extract (file exists) %s", destFile);
}
}
}
}
}

View File

@ -20,13 +20,12 @@ package org.eclipse.jetty.start;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.StandardCopyOption;
import java.util.HashSet;
import java.util.Set;
@ -143,23 +142,9 @@ public abstract class FileInitializer
throw new IOException("URL GET Failure [" + status + "/" + http.getResponseMessage() + "] on " + uri);
}
byte[] buf = new byte[8192];
try (InputStream in = http.getInputStream();
OutputStream out = Files.newOutputStream(destination, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))
try (InputStream in = http.getInputStream())
{
while (true)
{
int len = in.read(buf);
if (len > 0)
{
out.write(buf, 0, len);
}
if (len < 0)
{
break;
}
}
Files.copy(in, destination, StandardCopyOption.REPLACE_EXISTING);
}
}

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.jetty.start.BaseHome;
@ -105,7 +106,7 @@ public class MavenLocalRepoFileInitializer extends FileInitializer
}
}
private Path localRepositoryDir;
private final Path localRepositoryDir;
private final boolean readonly;
private String mavenRepoUri;
@ -116,19 +117,24 @@ public class MavenLocalRepoFileInitializer extends FileInitializer
public MavenLocalRepoFileInitializer(BaseHome baseHome, Path localRepoDir, boolean readonly)
{
super(baseHome, "maven");
this.localRepositoryDir = localRepoDir;
this.readonly = readonly;
this(baseHome, localRepoDir, readonly, null);
}
public MavenLocalRepoFileInitializer(BaseHome baseHome, Path localRepoDir, boolean readonly, String mavenRepoUri)
{
super(baseHome, "maven");
this.localRepositoryDir = localRepoDir;
this.localRepositoryDir = localRepoDir != null ? localRepoDir : newTempRepo();
this.readonly = readonly;
this.mavenRepoUri = mavenRepoUri;
}
private static Path newTempRepo()
{
Path javaTempDir = Paths.get(System.getProperty("java.io.tmpdir"));
// Simple return here, don't create the directory, unless it's being used.
return javaTempDir.resolve("jetty-start-downloads");
}
@Override
public boolean create(URI uri, String location) throws IOException
{
@ -139,16 +145,55 @@ public class MavenLocalRepoFileInitializer extends FileInitializer
return false;
}
Path destination = getDestination(uri, location);
URI destURI = URI.create(location);
if (destURI.isAbsolute() && destURI.getScheme().equals("extract"))
{
// Extract Flow
// Download to local repo.
Path localFile = localRepositoryDir.resolve(coords.toPath());
if (!FS.canReadFile(localFile))
{
if (FS.ensureDirectoryExists(localFile.getParent()))
StartLog.info("mkdir " + _basehome.toShortForm(localFile.getParent()));
download(coords, localFile);
if (!FS.canReadFile(localFile))
{
throw new IOException("Unable to establish temp copy of file to extract: " + localFile);
}
}
// Destination Directory
Path destination;
String extractLocation = destURI.getSchemeSpecificPart();
if (extractLocation.equals("/"))
{
destination = _basehome.getBasePath();
}
else
{
extractLocation = extractLocation.replaceFirst("^[/\\\\]*", "");
if (!extractLocation.endsWith("/"))
throw new IOException("Extract mode can only unpack to a directory, end your URL with a slash: " + location);
destination = _basehome.getBasePath().resolve(extractLocation);
if (Files.exists(destination) && !Files.isDirectory(destination))
throw new IOException("Destination already exists, and is not a directory: " + destination);
if (!destination.startsWith(_basehome.getBasePath()))
throw new IOException("For security reasons, Jetty start is unable to extract outside of the ${jetty.base} - " + location);
}
FS.extract(localFile, destination);
}
else
{
// Copy Flow
Path destination = getDestination(uri, location);
if (isFilePresent(destination))
return false;
// If using local repository
if (this.localRepositoryDir != null)
{
// Grab copy from local repository (download if needed to local
// repository)
// Grab copy from local repository (download if needed to local repository)
Path localRepoFile = getLocalRepoFile(coords);
if (localRepoFile != null)
@ -159,10 +204,11 @@ public class MavenLocalRepoFileInitializer extends FileInitializer
Files.copy(localRepoFile, destination);
return true;
}
}
// normal non-local repo version
download(coords, destination);
}
return true;
}

View File

@ -22,7 +22,6 @@ import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.eclipse.jetty.start.BaseHome;
import org.eclipse.jetty.start.config.ConfigSources;
@ -30,7 +29,6 @@ import org.eclipse.jetty.start.config.JettyBaseConfigSource;
import org.eclipse.jetty.start.config.JettyHomeConfigSource;
import org.eclipse.jetty.start.fileinits.MavenLocalRepoFileInitializer.Coordinates;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.junit.jupiter.api.BeforeEach;
@ -55,11 +53,15 @@ public class MavenLocalRepoFileInitializerTest
@BeforeEach
public void setupBaseHome() throws IOException
{
Path homeDir = testdir.getEmptyPathDir();
Path homeDir = testdir.getEmptyPathDir().resolve("home");
Path baseDir = testdir.getEmptyPathDir().resolve("base");
FS.ensureDirExists(homeDir);
FS.ensureDirExists(baseDir);
ConfigSources config = new ConfigSources();
config.add(new JettyHomeConfigSource(homeDir));
config.add(new JettyBaseConfigSource(homeDir));
config.add(new JettyBaseConfigSource(baseDir));
this.baseHome = new BaseHome(config);
}
@ -193,7 +195,7 @@ public class MavenLocalRepoFileInitializerTest
assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(),
is("https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-http/9.4.31.v20200723/jetty-http-9.4.31.v20200723-tests.jar"));
Path destination = Paths.get(System.getProperty("java.io.tmpdir"), "jetty-http-9.4.31.v20200723-tests.jar");
Path destination = testdir.getEmptyPathDir().resolve("jetty-http-9.4.31.v20200723-tests.jar");
Files.deleteIfExists(destination);
repo.download(coords.toCentralURI(), destination);
assertThat(Files.exists(destination), is(true));
@ -204,7 +206,7 @@ public class MavenLocalRepoFileInitializerTest
public void testDownloadSnapshotRepo()
throws Exception
{
Path snapshotLocalRepoDir = MavenTestingUtils.getTargetTestingPath("snapshot-repo");
Path snapshotLocalRepoDir = testdir.getPath().resolve("snapshot-repo");
FS.ensureEmpty(snapshotLocalRepoDir);
MavenLocalRepoFileInitializer repo =
@ -222,10 +224,43 @@ public class MavenLocalRepoFileInitializerTest
assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(),
is("https://oss.sonatype.org/content/repositories/jetty-snapshots/org/eclipse/jetty/jetty-rewrite/10.0.0-SNAPSHOT/jetty-rewrite-10.0.0-SNAPSHOT.jar"));
Path destination = Paths.get(System.getProperty("java.io.tmpdir"), "jetty-rewrite-10.0.0-SNAPSHOT.jar");
Files.deleteIfExists(destination);
Path destination = baseHome.getBasePath().resolve("jetty-rewrite-10.0.0-SNAPSHOT.jar");
repo.download(coords, destination);
assertThat(Files.exists(destination), is(true));
assertThat("Snapshot File size", destination.toFile().length(), greaterThan(10_000L));
}
@Test
public void testDownloadSnapshotRepoWithExtractDeep()
throws Exception
{
Path snapshotLocalRepoDir = testdir.getPath().resolve("snapshot-repo");
FS.ensureEmpty(snapshotLocalRepoDir);
MavenLocalRepoFileInitializer repo =
new MavenLocalRepoFileInitializer(baseHome, snapshotLocalRepoDir, false,
"https://oss.sonatype.org/content/repositories/jetty-snapshots/");
String ref = "maven://org.eclipse.jetty/test-jetty-webapp/10.0.0-SNAPSHOT/jar/config";
Path baseDir = baseHome.getBasePath();
repo.create(URI.create(ref), "extract:company/");
assertThat(Files.exists(baseDir.resolve("company/webapps/test.d/override-web.xml")), is(true));
}
@Test
public void testDownloadSnapshotRepoWithExtractDefault()
throws Exception
{
Path snapshotLocalRepoDir = testdir.getPath().resolve("snapshot-repo");
FS.ensureEmpty(snapshotLocalRepoDir);
MavenLocalRepoFileInitializer repo =
new MavenLocalRepoFileInitializer(baseHome, snapshotLocalRepoDir, false,
"https://oss.sonatype.org/content/repositories/jetty-snapshots/");
String ref = "maven://org.eclipse.jetty/test-jetty-webapp/10.0.0-SNAPSHOT/jar/config";
Path baseDir = baseHome.getBasePath();
repo.create(URI.create(ref), "extract:/");
assertThat(Files.exists(baseDir.resolve("webapps/test.d/override-web.xml")), is(true));
}
}