Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<jackson.version>3.0.4</jackson.version>
<apache.common-compress.version>1.28.0</apache.common-compress.version>
<zstd-jni.version>1.5.7-7</zstd-jni.version>
<xz.version>1.11</xz.version>
<bcprov-jdk18on.version>1.83</bcprov-jdk18on.version>

<!-- Test dependencies version -->
Expand Down Expand Up @@ -170,6 +171,11 @@
<artifactId>testcontainers-junit-jupiter</artifactId>
<version>${testcontainer.version}</version>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>${xz.version}</version>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock-standalone</artifactId>
Expand Down Expand Up @@ -209,6 +215,10 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
</dependency>
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/land/oras/utils/ArchiveUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream;
import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream;
import org.jspecify.annotations.NullMarked;
Expand Down Expand Up @@ -322,6 +323,22 @@ static LocalPath compressZstd(LocalPath tarFile) {
return LocalPath.of(tarGzFile, Const.BLOB_DIR_ZSTD_MEDIA_TYPE);
}

static LocalPath compressXz(LocalPath tarFile) {
LOG.trace("Compressing tar file to xz archive");
Path tarXzFile = Paths.get(tarFile.toString() + ".xz");
try (InputStream fis = Files.newInputStream(tarFile.getPath());
BufferedInputStream bis = new BufferedInputStream(fis);
OutputStream fos = Files.newOutputStream(tarXzFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
XZCompressorOutputStream xzos = new XZCompressorOutputStream(bos)) {

bis.transferTo(xzos);
} catch (IOException e) {
throw new OrasException("Failed to compress tar file to xz archive", e);
}
return LocalPath.of(tarXzFile, Const.BLOB_DIR_XZ_MEDIA_TYPE);
}

static LocalPath compressGzip(LocalPath tarFile) {
LOG.trace("Compressing tar file to gz archive");
Path tarGzFile = Paths.get(tarFile.toString() + ".gz");
Expand Down Expand Up @@ -353,6 +370,20 @@ static LocalPath uncompressGzip(InputStream inputStream) {
return LocalPath.of(tarFile, Const.DEFAULT_BLOB_MEDIA_TYPE);
}

static LocalPath uncompressXz(InputStream inputStream) {
LOG.trace("Uncompressing tar.xz file");
Path tarFile = createTempTar();
try (BufferedInputStream bis = new BufferedInputStream(inputStream);
XZCompressorOutputStream xzos =
new XZCompressorOutputStream(new BufferedOutputStream(Files.newOutputStream(tarFile)))) {

bis.transferTo(xzos);
} catch (IOException e) {
throw new OrasException("Failed to uncompress tar.xz file", e);
}
return LocalPath.of(tarFile, Const.DEFAULT_BLOB_MEDIA_TYPE);
}

static LocalPath uncompressZstd(InputStream inputStream) {
LOG.trace("Uncompressing zstd file");
Path tarFile = createTempTar();
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/land/oras/utils/Const.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ private Const() {
*/
public static final String BLOB_DIR_ZSTD_MEDIA_TYPE = "application/vnd.oci.image.layer.v1.tar+zstd";

/**
* The blob directory media type for xz compression
*/
public static final String BLOB_DIR_XZ_MEDIA_TYPE = "application/vnd.oci.image.layer.v1.tar+xz";

/**
* The default artifact media type if not specified
*/
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/land/oras/utils/SupportedCompression.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ public enum SupportedCompression {
/**
* ZSTD
*/
ZSTD(Const.BLOB_DIR_ZSTD_MEDIA_TYPE, ArchiveUtils::compressZstd, ArchiveUtils::uncompressZstd);
ZSTD(Const.BLOB_DIR_ZSTD_MEDIA_TYPE, ArchiveUtils::compressZstd, ArchiveUtils::uncompressZstd),

/**
* XZ
*/
XZ(Const.BLOB_DIR_XZ_MEDIA_TYPE, ArchiveUtils::compressXz, ArchiveUtils::uncompressXz);

/**
* The media type
Expand Down
23 changes: 23 additions & 0 deletions src/test/java/land/oras/utils/ArchiveUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class ArchiveUtilsTest {
@TempDir(cleanup = CleanupMode.ON_SUCCESS)
private static Path targetGzDir;

@TempDir(cleanup = CleanupMode.ON_SUCCESS)
private static Path targetXzDir;

@TempDir(cleanup = CleanupMode.ON_SUCCESS)
private static Path targetZstdDir;

Expand Down Expand Up @@ -208,6 +211,26 @@ void shouldExtractSeveralExistingArchive(String file) {
ArchiveUtils.uncompressuntar(archive, existingArchiveDir, SupportedCompression.GZIP.getMediaType());
}

@Test
void shouldCreateTarXzAndExtractIt() throws Exception {
LocalPath directory = LocalPath.of(archiveDir, Const.BLOB_DIR_XZ_MEDIA_TYPE);
LocalPath archive = ArchiveUtils.tar(directory);
LOG.info("Archive created: {}", archive);
Path compressedArchive =
ArchiveUtils.compress(archive, directory.getMediaType()).getPath();

assertTrue(Files.exists(compressedArchive), "Archive should exist");

Path uncompressedArchive = ArchiveUtils.uncompress(
Files.newInputStream(compressedArchive), Const.BLOB_DIR_XZ_MEDIA_TYPE)
.getPath();
ArchiveUtils.untar(Files.newInputStream(uncompressedArchive), targetXzDir);

// To temporary
Path temp = ArchiveUtils.uncompressuntar(compressedArchive, directory.getMediaType());
assertTrue(Files.exists(temp), "Temp should exist");
}

@Test
void shouldCreateTarZstdAndExtractIt() throws Exception {
LocalPath directory = LocalPath.of(archiveDir, Const.BLOB_DIR_ZSTD_MEDIA_TYPE);
Expand Down