Skip to content

Commit dff4f69

Browse files
Refactor SecureJar implementation and add package exclusions (#47)
* Add SecureJarBuilder * Split SecureJar implementation across new JarContents and JarSigningData * Add option to exclude some folder from the package scan * Restore not scanning META-INF for packages * Allow instantiating JarContents separately from the SecureJar * Remove SecureJarBuilder, JarContentsBuilder is enough * Use gradle.properties for dependency versions * Simplify readMultiReleaseInfo and add a test for it * Apply suggestions from code review Co-authored-by: Matyrobbrt <[email protected]> * Make ignoredRootPackages a Set in more cases * Replace BiPredicate<String,String> by UnionPathFilter in JarContentsBuilder * Move root package exclusion from JarContentsBuilder to getPackagesExcluding * Add some @deprecated annotations * Add since=2.1.16 to terminal deprecations --------- Co-authored-by: Matyrobbrt <[email protected]>
1 parent 44a5359 commit dff4f69

25 files changed

+725
-235
lines changed

build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,11 @@ group = 'cpw.mods'
117117
version = gradleutils.version
118118
logger.lifecycle('Version: ' + version)
119119

120-
ext.asmVersion = 9.3
121120
dependencies {
122-
api("org.ow2.asm:asm:${asmVersion}")
123-
api("org.ow2.asm:asm-tree:${asmVersion}")
124-
api("org.ow2.asm:asm-commons:${asmVersion}")
121+
api("org.ow2.asm:asm:${project.asm_version}")
122+
api("org.ow2.asm:asm-tree:${project.asm_version}")
123+
api("org.ow2.asm:asm-commons:${project.asm_version}")
124+
implementation("org.jetbrains:annotations:${project.jb_annotations_version}")
125125
testImplementation('org.junit.jupiter:junit-jupiter-api:5.8.+')
126126
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.8.+')
127127
}

gradle.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
asm_version=9.3
2+
jb_annotations_version=22.0.0

sjh-jmh/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ dependencies {
77
implementation('org.junit.jupiter:junit-jupiter-engine:5.8.+')
88
implementation('org.apache.logging.log4j:log4j-core:2.17.1')
99
implementation('org.apache.logging.log4j:log4j-api:2.17.1')
10-
implementation('org.ow2.asm:asm:9.3')
11-
implementation('org.ow2.asm:asm-tree:9.3')
12-
implementation('org.ow2.asm:asm-commons:9.3')
10+
implementation("org.ow2.asm:asm:${project.asm_version}")
11+
implementation("org.ow2.asm:asm-tree:${project.asm_version}")
12+
implementation("org.ow2.asm:asm-commons:${project.asm_version}")
1313
implementation('org.openjdk.jmh:jmh-core:1.35')
1414
jmhOnly('org.openjdk.jmh:jmh-core:1.35')
1515
jmhOnly('org.openjdk.jmh:jmh-generator-annprocess:1.35')
@@ -42,4 +42,4 @@ task jmh(type: JavaExec, dependsOn: sourceSets.main.output) {
4242
args '-f', '1' // forks
4343
args '-rff', project.file("${rootProject.buildDir}/jmh_results.txt") // results file
4444
args 'cpw.mods.niofs.union.benchmarks.UnionFileSystemBenchmark'
45-
}
45+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package cpw.mods.jarhandling;
2+
3+
import org.jetbrains.annotations.ApiStatus;
4+
5+
import java.net.URI;
6+
import java.nio.file.Path;
7+
import java.util.List;
8+
import java.util.Optional;
9+
import java.util.Set;
10+
import java.util.jar.Manifest;
11+
12+
/**
13+
* Access to the contents of a list of {@link Path}s, interpreted as a jar file.
14+
* Typically used to build the {@linkplain JarMetadata metadata} for a {@link SecureJar}.
15+
*
16+
* <p>Create with {@link JarContentsBuilder}.
17+
* Convert to a full jar with {@link SecureJar#from(JarContents)}.
18+
*/
19+
@ApiStatus.NonExtendable
20+
public interface JarContents {
21+
/**
22+
* @see SecureJar#getPrimaryPath()
23+
*/
24+
Path getPrimaryPath();
25+
26+
/**
27+
* Looks for a file in the jar.
28+
*/
29+
Optional<URI> findFile(String name);
30+
31+
/**
32+
* {@return the manifest of the jar}
33+
* Empty if no manifest is present in the jar.
34+
*/
35+
Manifest getManifest();
36+
37+
/**
38+
* {@return all the packages in the jar}
39+
* (Every folder containing a {@code .class} file is considered a package.)
40+
*/
41+
Set<String> getPackages();
42+
43+
/**
44+
* {@return all the packages in the jar, with some root packages excluded}
45+
*
46+
* <p>This can be used to skip scanning of folders that are known to not contain code,
47+
* but would be expensive to go through.
48+
*/
49+
Set<String> getPackagesExcluding(String... excludedRootPackages);
50+
51+
/**
52+
* Parses the {@code META-INF/services} files in the jar, and returns the list of service providers.
53+
*/
54+
List<SecureJar.Provider> getMetaInfServices();
55+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cpw.mods.jarhandling;
2+
3+
import cpw.mods.jarhandling.impl.JarContentsImpl;
4+
import cpw.mods.niofs.union.UnionPathFilter;
5+
import org.jetbrains.annotations.Nullable;
6+
7+
import java.nio.file.Path;
8+
import java.util.Objects;
9+
import java.util.function.Supplier;
10+
import java.util.jar.Manifest;
11+
12+
/**
13+
* Builder for {@link JarContents}.
14+
*/
15+
public final class JarContentsBuilder {
16+
private Path[] paths = new Path[0];
17+
private Supplier<Manifest> defaultManifest = Manifest::new;
18+
@Nullable
19+
private UnionPathFilter pathFilter = null;
20+
21+
public JarContentsBuilder() {}
22+
23+
/**
24+
* Sets the root paths for the files of this jar.
25+
*/
26+
public JarContentsBuilder paths(Path... paths) {
27+
this.paths = paths;
28+
return this;
29+
}
30+
31+
/**
32+
* Overrides the default manifest for this jar.
33+
* The default manifest is only used when the jar does not provide a manifest already.
34+
*/
35+
public JarContentsBuilder defaultManifest(Supplier<Manifest> manifest) {
36+
Objects.requireNonNull(manifest);
37+
38+
this.defaultManifest = manifest;
39+
return this;
40+
}
41+
42+
/**
43+
* Overrides the path filter for this jar, to exclude some entries from the underlying file system.
44+
*
45+
* @see UnionPathFilter
46+
*/
47+
public JarContentsBuilder pathFilter(@Nullable UnionPathFilter pathFilter) {
48+
this.pathFilter = pathFilter;
49+
return this;
50+
}
51+
52+
/**
53+
* Builds the jar.
54+
*/
55+
public JarContents build() {
56+
return new JarContentsImpl(paths, defaultManifest, pathFilter == null ? null : pathFilter::test);
57+
}
58+
}

src/main/java/cpw/mods/jarhandling/JarMetadata.java

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,31 @@ public interface JarMetadata {
3333
"volatile","const","float","native","super","while");
3434
Pattern KEYWORD_PARTS = Pattern.compile("(?<=^|\\.)(" + String.join("|", ILLEGAL_KEYWORDS) + ")(?=\\.|$)");
3535

36-
static JarMetadata from(final SecureJar jar, final Path... path) {
37-
if (path.length==0) throw new IllegalArgumentException("Need at least one path");
36+
/**
37+
* Builds the jar metadata for a jar following the normal rules for Java jars.
38+
*
39+
* <p>If the jar has a {@code module-info.class} file, the module info is read from there.
40+
* Otherwise, the jar is an automatic module, whose name is optionally derived
41+
* from {@code Automatic-Module-Name} in the manifest.
42+
*/
43+
static JarMetadata from(JarContents jar) {
3844
final var pkgs = jar.getPackages();
39-
var mi = jar.moduleDataProvider().findFile("module-info.class");
45+
var mi = jar.findFile("module-info.class");
4046
if (mi.isPresent()) {
4147
return new ModuleJarMetadata(mi.get(), pkgs);
4248
} else {
43-
var providers = jar.getProviders();
44-
var fileCandidate = fromFileName(path[0], pkgs, providers);
45-
var aname = jar.moduleDataProvider().getManifest().getMainAttributes().getValue("Automatic-Module-Name");
49+
var providers = jar.getMetaInfServices();
50+
var fileCandidate = fromFileName(jar.getPrimaryPath(), pkgs, providers);
51+
var aname = jar.getManifest().getMainAttributes().getValue("Automatic-Module-Name");
4652
if (aname != null) {
4753
return new SimpleJarMetadata(aname, fileCandidate.version(), pkgs, providers);
4854
} else {
4955
return fileCandidate;
5056
}
5157
}
5258
}
53-
static SimpleJarMetadata fromFileName(final Path path, final Set<String> pkgs, final List<SecureJar.Provider> providers) {
5459

60+
static SimpleJarMetadata fromFileName(final Path path, final Set<String> pkgs, final List<SecureJar.Provider> providers) {
5561
// detect Maven-like paths
5662
Path versionMaybe = path.getParent();
5763
if (versionMaybe != null)
@@ -137,4 +143,26 @@ private static String cleanModuleName(String mn) {
137143

138144
return mn;
139145
}
146+
147+
/**
148+
* @deprecated Use {@link #from(JarContents)} instead.
149+
*/
150+
@Deprecated(forRemoval = true, since = "2.1.16")
151+
static JarMetadata from(final SecureJar jar, final Path... path) {
152+
if (path.length==0) throw new IllegalArgumentException("Need at least one path");
153+
final var pkgs = jar.getPackages();
154+
var mi = jar.moduleDataProvider().findFile("module-info.class");
155+
if (mi.isPresent()) {
156+
return new ModuleJarMetadata(mi.get(), pkgs);
157+
} else {
158+
var providers = jar.getProviders();
159+
var fileCandidate = fromFileName(path[0], pkgs, providers);
160+
var aname = jar.moduleDataProvider().getManifest().getMainAttributes().getValue("Automatic-Module-Name");
161+
if (aname != null) {
162+
return new SimpleJarMetadata(aname, fileCandidate.version(), pkgs, providers);
163+
} else {
164+
return fileCandidate;
165+
}
166+
}
167+
}
140168
}

0 commit comments

Comments
 (0)