Skip to content

Commit e105eb8

Browse files
author
youngjin.kim2
committed
[#8890] Delegate parsing jar files into ModulePath
1 parent 4e8104b commit e105eb8

File tree

8 files changed

+469
-119
lines changed

8 files changed

+469
-119
lines changed

bootstraps/bootstrap-java9-internal/src/main/java/com/navercorp/pinpoint/bootstrap/java9/module/InternalModules.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,8 @@
1919
import jdk.internal.loader.BootLoader;
2020
import jdk.internal.module.Modules;
2121

22-
import java.lang.module.Configuration;
2322
import java.lang.module.ModuleDescriptor;
24-
import java.lang.module.ModuleFinder;
2523
import java.net.URI;
26-
import java.util.List;
27-
import java.util.Objects;
28-
import java.util.Set;
2924

3025
final class InternalModules {
3126

@@ -52,17 +47,7 @@ static Module defineModule(ClassLoader loader,
5247
ModuleDescriptor descriptor,
5348
URI uri)
5449
{
55-
final String moduleName = descriptor.name();
56-
final ModuleLayer parent = ModuleLayer.boot();
57-
58-
final ModuleFinder before = new SingleModuleFinder(descriptor, uri);
59-
final Configuration cf = parent.configuration().resolve(before, ModuleFinder.of(), Set.of(moduleName));
60-
final Module module = ModuleLayer.defineModules(cf, List.of(parent), name -> loader)
61-
.layer()
62-
.findModule(moduleName)
63-
.orElse(null);
64-
65-
return Objects.requireNonNull(module, moduleName);
50+
return Modules.defineModule(loader, descriptor, uri);
6651
}
6752

6853

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright 2022 NAVER Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.navercorp.pinpoint.bootstrap.java9.module;
17+
18+
import java.io.IOException;
19+
import java.io.InputStream;
20+
import java.util.Iterator;
21+
import java.util.Objects;
22+
import java.util.Optional;
23+
import java.util.function.Supplier;
24+
25+
/**
26+
* @author youngjin.kim2
27+
*/
28+
public class ConcatInputStream extends InputStream {
29+
30+
private final Iterator<Supplier<Optional<InputStream>>> it;
31+
private byte[] current;
32+
private int cursor = 0;
33+
34+
public ConcatInputStream(Iterable<Supplier<Optional<InputStream>>> children) {
35+
this.it = children.iterator();
36+
}
37+
38+
@Override
39+
public int read() throws IOException {
40+
if (Objects.isNull(current) || cursor >= current.length) {
41+
if (!augment()) {
42+
return -1;
43+
}
44+
}
45+
return current[cursor++];
46+
}
47+
48+
private boolean augment() throws IOException {
49+
while (it.hasNext()) {
50+
final Optional<InputStream> stream = it.next().get();
51+
if (!stream.isPresent()) {
52+
continue;
53+
}
54+
55+
current = stream.get().readAllBytes();
56+
cursor = 0;
57+
return true;
58+
}
59+
60+
return false;
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2022 NAVER Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.navercorp.pinpoint.bootstrap.java9.module;
17+
18+
import java.lang.module.ModuleDescriptor;
19+
import java.lang.module.ModuleReader;
20+
import java.lang.module.ModuleReference;
21+
import java.net.URI;
22+
23+
/**
24+
* @author youngjin.kim2
25+
*/
26+
class FixedReaderModuleReference extends ModuleReference {
27+
28+
private final ModuleReader moduleReader;
29+
30+
public FixedReaderModuleReference(ModuleDescriptor desc, URI uri, ModuleReader moduleReader) {
31+
super(desc, uri);
32+
this.moduleReader = moduleReader;
33+
}
34+
35+
@Override
36+
public ModuleReader open() {
37+
return moduleReader;
38+
}
39+
40+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright 2022 NAVER Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.navercorp.pinpoint.bootstrap.java9.module;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.io.InputStream;
21+
import java.net.URI;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import java.util.Enumeration;
25+
import java.util.Objects;
26+
import java.util.concurrent.atomic.AtomicInteger;
27+
import java.util.jar.Attributes;
28+
import java.util.jar.JarEntry;
29+
import java.util.jar.JarFile;
30+
import java.util.jar.Manifest;
31+
import java.util.zip.ZipOutputStream;
32+
33+
/**
34+
* Some libraries may contains illegal automatic module name.
35+
* @author youngjin.kim2
36+
*/
37+
public class JarCleaner {
38+
39+
private static final String AUTOMATIC_MODULE_NAME = "Automatic-Module-Name";
40+
private static final String cleanModuleNamePrefix = "pinpoint.dep";
41+
42+
private static final AtomicInteger tempCounter = new AtomicInteger(0);
43+
44+
/**
45+
* Re-produces new jar file which has exactly same content in same entries,
46+
* but only value of automatic-module-name in manifest.mf is replaced with "pinpoint.dep[\d]*".
47+
* <br><br>
48+
* Some of libraries may have illegal `Automatic-Module-Name` in their `MANIFEST.MF`, and this
49+
* method prevents that those attributes ruins validation in loading modules by replacing the
50+
* attribute
51+
* <br><br>
52+
* CAUTION: The original jarFile in `uri` is removed, and replaced with new one in success.
53+
*
54+
* @param uri the location of original jar file
55+
*/
56+
public static void clean(URI uri) {
57+
try {
58+
doCleanByURI(uri);
59+
} catch (IOException e) {
60+
throw new RuntimeException(e);
61+
}
62+
}
63+
64+
private static void doCleanByURI(URI uri) throws IOException {
65+
if (!uri.toString().endsWith(".jar")) {
66+
return;
67+
}
68+
69+
try (final JarFile jarFile = new JarFile(new File(uri))) {
70+
if (isDirty(jarFile)) {
71+
rewriteAutomaticModuleName(jarFile, uri);
72+
}
73+
}
74+
}
75+
76+
private static boolean isDirty(JarFile jarFile) throws IOException {
77+
final Manifest manifest = jarFile.getManifest();
78+
if (Objects.isNull(manifest)) {
79+
return true;
80+
}
81+
82+
final Attributes attrs = manifest.getMainAttributes();
83+
if (Objects.isNull(attrs)) {
84+
return true;
85+
}
86+
87+
final String moduleName = attrs.getValue(AUTOMATIC_MODULE_NAME);
88+
if (Objects.isNull(moduleName)) {
89+
return true;
90+
}
91+
92+
return !moduleName.startsWith(cleanModuleNamePrefix);
93+
}
94+
95+
private static void rewriteAutomaticModuleName(JarFile jarFile, URI uri) throws IOException {
96+
final Path outPath = Files.createTempFile("pinpoint_tmp_", ".jar");
97+
final ZipOutputStream outStream = new ZipOutputStream(Files.newOutputStream(outPath.toFile().toPath()));
98+
99+
final Enumeration<JarEntry> entries = jarFile.entries();
100+
while (entries.hasMoreElements()) {
101+
final JarEntry entry = entries.nextElement();
102+
103+
final InputStream inputStream = jarFile.getInputStream(entry);
104+
final byte[] bytes = transform(entry.getName(), inputStream.readAllBytes());
105+
inputStream.close();
106+
107+
outStream.putNextEntry(new JarEntry(entry.getName()));
108+
outStream.write(bytes);
109+
outStream.closeEntry();
110+
}
111+
112+
outStream.close();
113+
if (!outPath.toFile().renameTo(new File(uri))) {
114+
throw new RuntimeException("Failed to rename lib jar");
115+
}
116+
}
117+
118+
private static byte[] transform(String name, byte[] bytes) {
119+
if (!name.contains("MANIFEST.MF")) {
120+
return bytes;
121+
}
122+
123+
final String raw = new String(bytes);
124+
final String[] lines = raw.replaceAll("\r", "").split("\n");
125+
126+
final StringBuilder builder = new StringBuilder();
127+
for (final String line: lines) {
128+
if (line.isEmpty() || line.toLowerCase().startsWith(AUTOMATIC_MODULE_NAME.toLowerCase())) {
129+
continue;
130+
}
131+
builder.append(line).append("\r\n");
132+
}
133+
builder.append(AUTOMATIC_MODULE_NAME).append(": ").append(nextModuleName())
134+
.append("\r\n\r\n");
135+
return builder.toString().getBytes();
136+
}
137+
138+
private static String nextModuleName() {
139+
return cleanModuleNamePrefix + tempCounter.incrementAndGet();
140+
}
141+
142+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2022 NAVER Corp.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.navercorp.pinpoint.bootstrap.java9.module;
17+
18+
import java.io.IOException;
19+
import java.io.InputStream;
20+
import java.lang.module.ModuleReader;
21+
import java.lang.module.ModuleReference;
22+
import java.net.URI;
23+
import java.util.*;
24+
import java.util.function.Supplier;
25+
import java.util.stream.Stream;
26+
27+
class MergedModuleReader implements ModuleReader {
28+
29+
private final Set<String> entries = new HashSet<>();
30+
private final Map<String, Set<ModuleReference>> nameToModuleRefs = new HashMap<>();
31+
32+
public MergedModuleReader(Iterable<ModuleReference> moduleRefs) throws IOException {
33+
for (final ModuleReference ref : moduleRefs) {
34+
try (final ModuleReader reader = ref.open()) {
35+
reader.list().forEach(name -> {
36+
entries.add(name);
37+
nameToModuleRefs.computeIfAbsent(name, k -> new HashSet<>()).add(ref);
38+
});
39+
}
40+
}
41+
}
42+
43+
@Override
44+
public Optional<URI> find(String name) throws IOException {
45+
for (final ModuleReference ref : nameToModuleRefs.get(name)) {
46+
try (final ModuleReader subModuleFinder = ref.open()) {
47+
final Optional<URI> result = subModuleFinder.find(name);
48+
if (result.isPresent()) {
49+
return result;
50+
}
51+
}
52+
}
53+
return Optional.empty();
54+
}
55+
56+
@Override
57+
public Stream<String> list() throws IOException {
58+
return entries.stream();
59+
}
60+
61+
@Override
62+
public void close() throws IOException {
63+
}
64+
65+
@Override
66+
public Optional<InputStream> open(String name) throws IOException {
67+
if (!entries.contains(name)) {
68+
return Optional.empty();
69+
}
70+
71+
return Optional.of(buildInputStream(name));
72+
}
73+
74+
private InputStream buildInputStream(String name) {
75+
final List<Supplier<Optional<InputStream>>> children = new ArrayList<>();
76+
for (final ModuleReference ref: nameToModuleRefs.get(name)) {
77+
children.add(() -> {
78+
try {
79+
return ref.open().open(name);
80+
} catch (IOException e) {
81+
throw new RuntimeException("Failed to open child", e);
82+
}
83+
});
84+
}
85+
return new ConcatInputStream(children);
86+
}
87+
88+
}

0 commit comments

Comments
 (0)