Skip to content

Commit 1a20d7c

Browse files
authored
Merge pull request #83 from BenCodez/codex/coordinated-jar-size
Add thin artifact for coordinated shading
2 parents 1195bf9 + 385dada commit 1a20d7c

4 files changed

Lines changed: 56 additions & 20 deletions

File tree

‎SimpleAPI/pom.xml‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@
9797
</includes>
9898
</configuration>
9999
</execution>
100+
<execution>
101+
<id>thin-library</id>
102+
<phase>package</phase>
103+
<goals><goal>jar</goal></goals>
104+
<configuration>
105+
<classifier>thin</classifier>
106+
</configuration>
107+
</execution>
100108
<execution>
101109
<id>shared-library-sources</id>
102110
<phase>package</phase>
@@ -149,14 +157,8 @@
149157
<exclude>META-INF/*.SF</exclude>
150158
<exclude>META-INF/*.DSA</exclude>
151159
<exclude>META-INF/*.RSA</exclude>
152-
</excludes>
153-
</filter>
154-
<!-- The full artifact is not a multi-release JAR. Keep every
155-
base BC class (including reflective provider mappings), but
156-
do not ship versioned implementations it cannot select. -->
157-
<filter>
158-
<artifact>org.bouncycastle:*</artifact>
159-
<excludes>
160+
<!-- The final artifact deliberately is not multi-release,
161+
so these implementations are unreachable. -->
160162
<exclude>META-INF/versions/**</exclude>
161163
</excludes>
162164
</filter>
@@ -206,6 +208,7 @@
206208
<reportsDirectory>${project.build.directory}/full-artifact-reports</reportsDirectory>
207209
<systemPropertyVariables>
208210
<simpleapi.fullJar>${project.build.directory}/${project.build.finalName}.jar</simpleapi.fullJar>
211+
<simpleapi.thinJar>${project.build.directory}/${project.build.finalName}-thin.jar</simpleapi.thinJar>
209212
</systemPropertyVariables>
210213
</configuration>
211214
</execution>

‎SimpleAPI/src/test/java/com/bencodez/simpleapi/tests/BungeeJsonFileTest.java‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
import static org.junit.jupiter.api.Assertions.assertTrue;
77

88
import java.io.File;
9+
import java.nio.file.Path;
910
import java.util.Arrays;
1011
import java.util.List;
1112

1213
import org.junit.jupiter.api.AfterAll;
1314
import org.junit.jupiter.api.BeforeAll;
1415
import org.junit.jupiter.api.Test;
1516
import org.junit.jupiter.api.TestInstance;
17+
import org.junit.jupiter.api.io.TempDir;
1618

1719
import com.bencodez.simpleapi.file.BungeeJsonFile;
1820
import com.google.gson.JsonObject;
@@ -24,9 +26,9 @@ public class BungeeJsonFileTest {
2426
private BungeeJsonFile bungeeJsonFile;
2527

2628
@BeforeAll
27-
public void setup() {
29+
public void setup(@TempDir Path temporary) {
2830
// Create a temporary file for testing
29-
testFile = new File("test.json");
31+
testFile = temporary.resolve("test.json").toFile();
3032
bungeeJsonFile = new BungeeJsonFile(testFile);
3133
}
3234

‎SimpleAPI/src/test/java/com/bencodez/simpleapi/tests/packaging/FullArtifactTest.java‎

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
public class FullArtifactTest {
2424
@TempDir Path temporary;
2525

26-
@Test void retainsBaseCryptoClassesWithoutUnusedVersionedPayload() throws Exception {
26+
@Test void retainsBaseCryptoClassesWithoutUnusableVersionedPayload() throws Exception {
2727
Path full = fullJar();
2828
long removedEntries = 0;
2929
long removedCompressedBytes = 0;
@@ -35,8 +35,8 @@ public class FullArtifactTest {
3535
"Revisit the BC filter before making the full artifact multi-release");
3636
String classPath = output.getManifest().getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
3737
assertTrue(classPath == null || classPath.isBlank(), "Smoke test must not load external manifest dependencies");
38-
assertFalse(output.stream().anyMatch(entry -> entry.getName().startsWith("META-INF/versions/")
39-
&& entry.getName().contains("/org/bouncycastle/")), "Unused versioned BC payload is still bundled");
38+
assertFalse(output.stream().anyMatch(entry -> entry.getName().startsWith("META-INF/versions/")),
39+
"A non-multi-release artifact must not bundle unreachable versioned implementations");
4040

4141
// Resolve all three original libraries from Maven, without a pinned version or ~/.m2 path.
4242
for (Class<?> anchor : List.of(BouncyCastleProvider.class, X509CertificateHolder.class, ContentInfo.class)) {
@@ -62,6 +62,21 @@ public class FullArtifactTest {
6262
Files.size(full), retainedEntries, removedEntries, removedCompressedBytes);
6363
}
6464

65+
@Test void thinArtifactContainsProjectClassesWithoutEmbeddedDependencies() throws Exception {
66+
Path thin = thinJar();
67+
try (JarFile artifact = new JarFile(thin.toFile())) {
68+
assertNotNull(artifact.getEntry("com/bencodez/simpleapi/servercomm/http/HttpTlsIdentity.class"),
69+
"Thin artifact must preserve the complete SimpleAPI API");
70+
assertNotNull(artifact.getEntry("com/bencodez/simpleapi/scheduler/BukkitScheduler.class"));
71+
assertNull(artifact.getEntry("org/bouncycastle/jce/provider/BouncyCastleProvider.class"));
72+
assertNull(artifact.getEntry("com/zaxxer/hikari/HikariDataSource.class"));
73+
assertNull(artifact.getEntry("redis/clients/jedis/Jedis.class"));
74+
assertNull(artifact.getEntry("org/spongepowered/configurate/ConfigurationNode.class"));
75+
assertFalse(artifact.stream().anyMatch(entry -> entry.getName().startsWith("META-INF/versions/")));
76+
}
77+
System.out.printf("Thin artifact: %,d bytes (project classes only)%n", Files.size(thin));
78+
}
79+
6580
@Test void packagedTlsWorksWithoutMavenDependencies() throws Exception {
6681
Path full = fullJar();
6782
String fixtureName = PackagedTlsSmoke.class.getName();
@@ -104,4 +119,12 @@ private static Path fullJar() {
104119
assertTrue(Files.isRegularFile(full), "Missing packaged full artifact: " + full);
105120
return full;
106121
}
122+
123+
private static Path thinJar() {
124+
String value = System.getProperty("simpleapi.thinJar");
125+
assertNotNull(value, "Run this test through the Maven package lifecycle");
126+
Path thin = Path.of(value).toAbsolutePath().normalize();
127+
assertTrue(Files.isRegularFile(thin), "Missing packaged thin artifact: " + thin);
128+
return thin;
129+
}
107130
}

‎docs/jar-packaging.md‎

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@
44
`shared-sources` classifiers, dependency scopes, and public APIs are unchanged.
55
There are no additional modules or runtime downloads.
66

7+
The `thin` classifier contains the complete, unshaded SimpleAPI classes and
8+
resources but no embedded third-party classes. It exists for trusted downstream
9+
projects that immediately shade SimpleAPI while explicitly controlling the
10+
ordinary POM's transitive dependencies. It is not a standalone replacement for
11+
the full artifact. In particular, a consumer of HTTP/TLS APIs must still supply
12+
the declared Bouncy Castle libraries. Normal external consumers should continue
13+
to use the self-contained main artifact.
14+
715
The HTTP transport uses Bouncy Castle for its private CA and certificates.
816
Do not remove those dependencies, switch them to `provided`, strip provider
917
mappings, or enable broad `minimizeJar` without packaged-runtime validation.
1018
Providers load some implementation classes reflectively.
1119

12-
The full shaded artifact is not a multi-release JAR. Its BC-specific shade
13-
filter omits `META-INF/versions/**`, which that artifact cannot select, while
14-
retaining all base BC classes and resources. Other dependencies are not filtered
15-
by this rule. This is a conservative reduction of redundant payload, not removal
16-
of the crypto provider or the entire HTTP dependency cost.
20+
The full shaded artifact is not a multi-release JAR. Its shade filter omits
21+
`META-INF/versions/**`, which that artifact cannot select, while retaining base
22+
classes and resources. This is a conservative reduction of unreachable payload,
23+
not removal of the crypto provider or the entire HTTP dependency cost.
1724

1825
Java only selects versioned classes when the final manifest declares
1926
`Multi-Release: true`. If that contract changes, revisit this filter and the
@@ -31,8 +38,9 @@ git diff --check
3138

3239
The package phase runs `FullArtifactTest` after shading, followed by the existing
3340
shared-classpath tests. It verifies the non-multi-release manifest, absence of
34-
versioned BC payload, and preservation of every base `org/bouncycastle/` entry
35-
from the three resolved BC libraries. It prints the final JAR size and the
41+
all unreachable versioned payload, preservation of every base
42+
`org/bouncycastle/` entry from the three resolved BC libraries, and the thin
43+
artifact's no-embedded-dependencies contract. It prints the final JAR size and the
3644
compressed upstream payload omitted. That payload counter is not an exact
3745
before/after distribution size: shading/recompression and ZIP overhead differ.
3846
To measure the exact reduction, compare clean baseline and candidate builds with

0 commit comments

Comments
 (0)