Skip to content

Commit

Permalink
Remove Commons Lang dependency
Browse files Browse the repository at this point in the history
Commons Compress version 1.26.0 depends on Commons Lang, which is a 650 KiB dependency.
In reality, however, only 4 methods accounting for less than 1% of Commons Lang are used.

We replace the dependency on `commons-lang3` with a local copy of these 4 methods.
  • Loading branch information
ppkarwasz committed Nov 17, 2024
1 parent 8625b92 commit 212ff0e
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 140 deletions.
5 changes: 3 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,13 @@ Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.17.0</version>
</dependency>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.17.0</version>
</dependency>
<scope>test</scope>
</dependency>
</dependencies>

<scm>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.EntryStreamOffsets;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.internal.lang3.SystemProperties;
import org.apache.commons.compress.utils.ArchiveUtils;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.ParsingUtils;
import org.apache.commons.compress.utils.TimeUtils;
import org.apache.commons.io.file.attribute.FileTimes;
import org.apache.commons.lang3.SystemProperties;

/**
* An entry in a <a href="https://www.gnu.org/software/tar/manual/html_node/Standard.html">Tar archive</a>.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.internal.lang3.ArrayFill;
import org.apache.commons.compress.utils.Charsets;
import org.apache.commons.compress.utils.FixedLengthBlockOutputStream;
import org.apache.commons.compress.utils.TimeUtils;
import org.apache.commons.io.file.attribute.FileTimes;
import org.apache.commons.io.output.CountingOutputStream;
import org.apache.commons.lang3.ArrayFill;

/**
* The TarOutputStream writes a UNIX tar archive as an OutputStream. Methods are provided to put entries, and then write their contents by writing to this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import java.io.InputStream;

import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.ArrayFill;
import org.apache.commons.compress.internal.lang3.ArrayFill;

/**
* Binary tree of positive values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
import java.nio.ByteOrder;
import java.util.Arrays;

import org.apache.commons.compress.internal.lang3.ArrayFill;
import org.apache.commons.compress.utils.BitInputStream;
import org.apache.commons.compress.utils.ByteUtils;
import org.apache.commons.compress.utils.ExactMath;
import org.apache.commons.lang3.ArrayFill;

/**
* TODO This class can't be final because it is mocked by Mockito.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.io.IOException;
import java.util.Objects;

import org.apache.commons.lang3.ArrayFill;
import org.apache.commons.compress.internal.lang3.ArrayFill;

/**
* Helper class for compression algorithms that use the ideas of LZ77.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,164 +34,164 @@
import org.apache.commons.compress.java.util.jar.Pack200.Unpacker;
import org.apache.commons.io.input.BoundedInputStream;
import org.apache.commons.io.input.CloseShieldInputStream;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.compress.internal.lang3.reflect.FieldUtils;

/**
* This class provides the binding between the standard Pack200 interface and the internal interface for (un)packing.
*/
public class Pack200UnpackerAdapter extends Pack200Adapter implements Unpacker {

/**
* Creates a new BoundedInputStream bound by the size of the given file.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param file The file.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
*/
static BoundedInputStream newBoundedInputStream(final File file) throws IOException {
return newBoundedInputStream(file.toPath());
}
/**
* Creates a new BoundedInputStream bound by the size of the given file.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param file The file.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
*/
static BoundedInputStream newBoundedInputStream(final File file) throws IOException {
return newBoundedInputStream(file.toPath());
}

private static BoundedInputStream newBoundedInputStream(final FileInputStream fileInputStream) throws IOException {
return newBoundedInputStream(readPathString(fileInputStream));
}
private static BoundedInputStream newBoundedInputStream(final FileInputStream fileInputStream) throws IOException {
return newBoundedInputStream(readPathString(fileInputStream));
}

@SuppressWarnings("resource") // Caller closes.
static BoundedInputStream newBoundedInputStream(final InputStream inputStream) throws IOException {
if (inputStream instanceof BoundedInputStream) {
// Already bound.
return (BoundedInputStream) inputStream;
}
if (inputStream instanceof CloseShieldInputStream) {
// Don't unwrap to keep close shield.
return newBoundedInputStream(BoundedInputStream.builder().setInputStream(inputStream).get());
}
if (inputStream instanceof FilterInputStream) {
return newBoundedInputStream(unwrap((FilterInputStream) inputStream));
}
if (inputStream instanceof FileInputStream) {
return newBoundedInputStream((FileInputStream) inputStream);
}
// No limit
return newBoundedInputStream(BoundedInputStream.builder().setInputStream(inputStream).get());
@SuppressWarnings("resource") // Caller closes.
static BoundedInputStream newBoundedInputStream(final InputStream inputStream) throws IOException {
if (inputStream instanceof BoundedInputStream) {
// Already bound.
return (BoundedInputStream) inputStream;
}
if (inputStream instanceof CloseShieldInputStream) {
// Don't unwrap to keep close shield.
return newBoundedInputStream(BoundedInputStream.builder().setInputStream(inputStream).get());
}
if (inputStream instanceof FilterInputStream) {
return newBoundedInputStream(unwrap((FilterInputStream) inputStream));
}
if (inputStream instanceof FileInputStream) {
return newBoundedInputStream((FileInputStream) inputStream);
}
// No limit
return newBoundedInputStream(BoundedInputStream.builder().setInputStream(inputStream).get());
}

/**
* Creates a new BoundedInputStream bound by the size of the given path.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param path The path.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
*/
@SuppressWarnings("resource") // Caller closes.
static BoundedInputStream newBoundedInputStream(final Path path) throws IOException {
// @formatter:off
/**
* Creates a new BoundedInputStream bound by the size of the given path.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param path The path.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
*/
@SuppressWarnings("resource") // Caller closes.
static BoundedInputStream newBoundedInputStream(final Path path) throws IOException {
// @formatter:off
return BoundedInputStream.builder()
.setInputStream(new BufferedInputStream(Files.newInputStream(path)))
.setMaxCount(Files.size(path))
.setPropagateClose(false)
.get();
// @formatter:on
}
}

/**
* Creates a new BoundedInputStream bound by the size of the given file.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param first the path string or initial part of the path string.
* @param more additional strings to be joined to form the path string.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
*/
static BoundedInputStream newBoundedInputStream(final String first, final String... more) throws IOException {
return newBoundedInputStream(Paths.get(first, more));
}
/**
* Creates a new BoundedInputStream bound by the size of the given file.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param first the path string or initial part of the path string.
* @param more additional strings to be joined to form the path string.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
*/
static BoundedInputStream newBoundedInputStream(final String first, final String... more) throws IOException {
return newBoundedInputStream(Paths.get(first, more));
}

/**
* Creates a new BoundedInputStream bound by the size of the given URL to a file.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param url The URL.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
* @throws URISyntaxException
*/
static BoundedInputStream newBoundedInputStream(final URL url) throws IOException, URISyntaxException {
return newBoundedInputStream(Paths.get(url.toURI()));
}
/**
* Creates a new BoundedInputStream bound by the size of the given URL to a file.
* <p>
* The new BoundedInputStream wraps a new {@link BufferedInputStream}.
* </p>
*
* @param url The URL.
* @return a new BoundedInputStream
* @throws IOException if an I/O error occurs
* @throws URISyntaxException
*/
static BoundedInputStream newBoundedInputStream(final URL url) throws IOException, URISyntaxException {
return newBoundedInputStream(Paths.get(url.toURI()));
}

@SuppressWarnings("unchecked")
private static <T> T readField(final Object object, final String fieldName) {
try {
return (T) FieldUtils.readField(object, fieldName, true);
} catch (final IllegalAccessException e) {
return null;
}
@SuppressWarnings("unchecked")
private static <T> T readField(final Object object, final String fieldName) {
try {
return (T) FieldUtils.readField(object, fieldName, true);
} catch (final IllegalAccessException e) {
return null;
}
}

static String readPathString(final FileInputStream fis) {
return readField(fis, "path");
}
static String readPathString(final FileInputStream fis) {
return readField(fis, "path");
}

/**
* Unwraps the given FilterInputStream to return its wrapped InputStream.
*
* @param filterInputStream The FilterInputStream to unwrap.
* @return The wrapped InputStream
*/
static InputStream unwrap(final FilterInputStream filterInputStream) {
return readField(filterInputStream, "in");
}
/**
* Unwraps the given FilterInputStream to return its wrapped InputStream.
*
* @param filterInputStream The FilterInputStream to unwrap.
* @return The wrapped InputStream
*/
static InputStream unwrap(final FilterInputStream filterInputStream) {
return readField(filterInputStream, "in");
}

/**
* Unwraps the given InputStream if it is an FilterInputStream to return its wrapped InputStream.
*
* @param inputStream The FilterInputStream to unwrap.
* @return The wrapped InputStream
*/
static InputStream unwrap(final InputStream inputStream) {
return inputStream instanceof FilterInputStream ? unwrap((FilterInputStream) inputStream) : inputStream;
}
/**
* Unwraps the given InputStream if it is an FilterInputStream to return its wrapped InputStream.
*
* @param inputStream The FilterInputStream to unwrap.
* @return The wrapped InputStream
*/
static InputStream unwrap(final InputStream inputStream) {
return inputStream instanceof FilterInputStream ? unwrap((FilterInputStream) inputStream) : inputStream;
}

@Override
public void unpack(final File file, final JarOutputStream out) throws IOException {
if (file == null) {
throw new IllegalArgumentException("Must specify input file.");
}
if (out == null) {
throw new IllegalArgumentException("Must specify output stream.");
}
final long size = file.length();
final int bufferSize = size > 0 && size < DEFAULT_BUFFER_SIZE ? (int) size : DEFAULT_BUFFER_SIZE;
try (InputStream in = new BufferedInputStream(Files.newInputStream(file.toPath()), bufferSize)) {
unpack(in, out);
}
@Override
public void unpack(final File file, final JarOutputStream out) throws IOException {
if (file == null) {
throw new IllegalArgumentException("Must specify input file.");
}
if (out == null) {
throw new IllegalArgumentException("Must specify output stream.");
}
final long size = file.length();
final int bufferSize = size > 0 && size < DEFAULT_BUFFER_SIZE ? (int) size : DEFAULT_BUFFER_SIZE;
try (InputStream in = new BufferedInputStream(Files.newInputStream(file.toPath()), bufferSize)) {
unpack(in, out);
}
}

@Override
public void unpack(final InputStream in, final JarOutputStream out) throws IOException {
if (in == null) {
throw new IllegalArgumentException("Must specify input stream.");
}
if (out == null) {
throw new IllegalArgumentException("Must specify output stream.");
}
completed(0);
try {
new Archive(in, out).unpack();
} catch (final Pack200Exception e) {
throw new IOException("Failed to unpack Jar:" + e);
}
completed(1);
@Override
public void unpack(final InputStream in, final JarOutputStream out) throws IOException {
if (in == null) {
throw new IllegalArgumentException("Must specify input stream.");
}
if (out == null) {
throw new IllegalArgumentException("Must specify output stream.");
}
completed(0);
try {
new Archive(in, out).unpack();
} catch (final Pack200Exception e) {
throw new IOException("Failed to unpack Jar:" + e);
}
completed(1);
}
}
Loading

0 comments on commit 212ff0e

Please sign in to comment.