Issue #5264 - Supporting extract of maven archive to destination
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
parent
e338310c0a
commit
ba78355bd6
|
@ -21,11 +21,15 @@ package org.eclipse.jetty.start;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.attribute.FileTime;
|
import java.nio.file.attribute.FileTime;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
public class FS
|
public class FS
|
||||||
{
|
{
|
||||||
|
@ -168,4 +172,52 @@ public class FS
|
||||||
{
|
{
|
||||||
return path.toRealPath();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,12 @@ package org.eclipse.jetty.start;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -143,23 +142,9 @@ public abstract class FileInitializer
|
||||||
throw new IOException("URL GET Failure [" + status + "/" + http.getResponseMessage() + "] on " + uri);
|
throw new IOException("URL GET Failure [" + status + "/" + http.getResponseMessage() + "] on " + uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] buf = new byte[8192];
|
try (InputStream in = http.getInputStream())
|
||||||
try (InputStream in = http.getInputStream();
|
|
||||||
OutputStream out = Files.newOutputStream(destination, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE))
|
|
||||||
{
|
{
|
||||||
while (true)
|
Files.copy(in, destination, StandardCopyOption.REPLACE_EXISTING);
|
||||||
{
|
|
||||||
int len = in.read(buf);
|
|
||||||
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
out.write(buf, 0, len);
|
|
||||||
}
|
|
||||||
if (len < 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
import org.eclipse.jetty.start.BaseHome;
|
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 final boolean readonly;
|
||||||
private String mavenRepoUri;
|
private String mavenRepoUri;
|
||||||
|
|
||||||
|
@ -116,19 +117,24 @@ public class MavenLocalRepoFileInitializer extends FileInitializer
|
||||||
|
|
||||||
public MavenLocalRepoFileInitializer(BaseHome baseHome, Path localRepoDir, boolean readonly)
|
public MavenLocalRepoFileInitializer(BaseHome baseHome, Path localRepoDir, boolean readonly)
|
||||||
{
|
{
|
||||||
super(baseHome, "maven");
|
this(baseHome, localRepoDir, readonly, null);
|
||||||
this.localRepositoryDir = localRepoDir;
|
|
||||||
this.readonly = readonly;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MavenLocalRepoFileInitializer(BaseHome baseHome, Path localRepoDir, boolean readonly, String mavenRepoUri)
|
public MavenLocalRepoFileInitializer(BaseHome baseHome, Path localRepoDir, boolean readonly, String mavenRepoUri)
|
||||||
{
|
{
|
||||||
super(baseHome, "maven");
|
super(baseHome, "maven");
|
||||||
this.localRepositoryDir = localRepoDir;
|
this.localRepositoryDir = localRepoDir != null ? localRepoDir : newTempRepo();
|
||||||
this.readonly = readonly;
|
this.readonly = readonly;
|
||||||
this.mavenRepoUri = mavenRepoUri;
|
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
|
@Override
|
||||||
public boolean create(URI uri, String location) throws IOException
|
public boolean create(URI uri, String location) throws IOException
|
||||||
{
|
{
|
||||||
|
@ -139,16 +145,55 @@ public class MavenLocalRepoFileInitializer extends FileInitializer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path destination = getDestination(uri, location);
|
URI destURI = URI.create(location);
|
||||||
|
if (destURI.isAbsolute() && destURI.getScheme().equals("extract"))
|
||||||
if (isFilePresent(destination))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If using local repository
|
|
||||||
if (this.localRepositoryDir != null)
|
|
||||||
{
|
{
|
||||||
// Grab copy from local repository (download if needed to local
|
// Extract Flow
|
||||||
// repository)
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Grab copy from local repository (download if needed to local repository)
|
||||||
Path localRepoFile = getLocalRepoFile(coords);
|
Path localRepoFile = getLocalRepoFile(coords);
|
||||||
|
|
||||||
if (localRepoFile != null)
|
if (localRepoFile != null)
|
||||||
|
@ -159,10 +204,11 @@ public class MavenLocalRepoFileInitializer extends FileInitializer
|
||||||
Files.copy(localRepoFile, destination);
|
Files.copy(localRepoFile, destination);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normal non-local repo version
|
||||||
|
download(coords, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
// normal non-local repo version
|
|
||||||
download(coords, destination);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.start.BaseHome;
|
import org.eclipse.jetty.start.BaseHome;
|
||||||
import org.eclipse.jetty.start.config.ConfigSources;
|
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.config.JettyHomeConfigSource;
|
||||||
import org.eclipse.jetty.start.fileinits.MavenLocalRepoFileInitializer.Coordinates;
|
import org.eclipse.jetty.start.fileinits.MavenLocalRepoFileInitializer.Coordinates;
|
||||||
import org.eclipse.jetty.toolchain.test.FS;
|
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.WorkDir;
|
||||||
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -55,11 +53,15 @@ public class MavenLocalRepoFileInitializerTest
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setupBaseHome() throws IOException
|
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();
|
ConfigSources config = new ConfigSources();
|
||||||
config.add(new JettyHomeConfigSource(homeDir));
|
config.add(new JettyHomeConfigSource(homeDir));
|
||||||
config.add(new JettyBaseConfigSource(homeDir));
|
config.add(new JettyBaseConfigSource(baseDir));
|
||||||
|
|
||||||
this.baseHome = new BaseHome(config);
|
this.baseHome = new BaseHome(config);
|
||||||
}
|
}
|
||||||
|
@ -193,7 +195,7 @@ public class MavenLocalRepoFileInitializerTest
|
||||||
assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(),
|
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"));
|
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);
|
Files.deleteIfExists(destination);
|
||||||
repo.download(coords.toCentralURI(), destination);
|
repo.download(coords.toCentralURI(), destination);
|
||||||
assertThat(Files.exists(destination), is(true));
|
assertThat(Files.exists(destination), is(true));
|
||||||
|
@ -204,7 +206,7 @@ public class MavenLocalRepoFileInitializerTest
|
||||||
public void testDownloadSnapshotRepo()
|
public void testDownloadSnapshotRepo()
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
Path snapshotLocalRepoDir = MavenTestingUtils.getTargetTestingPath("snapshot-repo");
|
Path snapshotLocalRepoDir = testdir.getPath().resolve("snapshot-repo");
|
||||||
FS.ensureEmpty(snapshotLocalRepoDir);
|
FS.ensureEmpty(snapshotLocalRepoDir);
|
||||||
|
|
||||||
MavenLocalRepoFileInitializer repo =
|
MavenLocalRepoFileInitializer repo =
|
||||||
|
@ -222,10 +224,43 @@ public class MavenLocalRepoFileInitializerTest
|
||||||
assertThat("coords.toCentralURI", coords.toCentralURI().toASCIIString(),
|
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"));
|
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");
|
Path destination = baseHome.getBasePath().resolve("jetty-rewrite-10.0.0-SNAPSHOT.jar");
|
||||||
Files.deleteIfExists(destination);
|
|
||||||
repo.download(coords, destination);
|
repo.download(coords, destination);
|
||||||
assertThat(Files.exists(destination), is(true));
|
assertThat(Files.exists(destination), is(true));
|
||||||
assertThat("Snapshot File size", destination.toFile().length(), greaterThan(10_000L));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue