Skip to content

Commit 84c4dd1

Browse files
committed
MRJAR-aware embedded loader
1 parent 5814576 commit 84c4dd1

File tree

1 file changed

+56
-7
lines changed

1 file changed

+56
-7
lines changed

libs/core/src/main/java/org/elasticsearch/core/internal/provider/EmbeddedImplClassLoader.java

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.io.InputStreamReader;
1515
import java.io.UncheckedIOException;
1616
import java.lang.module.ModuleFinder;
17+
import java.net.MalformedURLException;
1718
import java.net.URI;
1819
import java.net.URISyntaxException;
1920
import java.net.URL;
@@ -26,14 +27,18 @@
2627
import java.security.CodeSource;
2728
import java.security.PrivilegedAction;
2829
import java.security.SecureClassLoader;
30+
import java.util.ArrayList;
2931
import java.util.Collections;
3032
import java.util.Enumeration;
31-
import java.util.HashMap;
33+
import java.util.LinkedHashMap;
3234
import java.util.List;
3335
import java.util.Map;
3436
import java.util.NoSuchElementException;
3537
import java.util.Objects;
3638
import java.util.function.Function;
39+
import java.util.jar.Manifest;
40+
41+
import static java.util.jar.Attributes.Name.MULTI_RELEASE;
3742

3843
/**
3944
* A class loader that is responsible for loading implementation classes and resources embedded within an archive.
@@ -67,7 +72,7 @@ public final class EmbeddedImplClassLoader extends SecureClassLoader {
6772
private final Map<String, CodeSource> prefixToCodeBase;
6873

6974
private static final String IMPL_PREFIX = "IMPL-JARS/";
70-
private static final String MANIFEST_FILE = "/LISTING.TXT";
75+
private static final String JAR_LISTING_FILE = "/LISTING.TXT";
7176

7277
static EmbeddedImplClassLoader getInstance(ClassLoader parent, String providerName) {
7378
return new EmbeddedImplClassLoader(parent, getProviderPrefixes(parent, providerName));
@@ -152,6 +157,8 @@ protected Enumeration<URL> findResources(String name) throws IOException {
152157
return new CompoundEnumeration<>(tmp);
153158
}
154159

160+
// -- modules
161+
155162
/**
156163
* Returns a module finder capable of finding the modules that are loadable by this embedded impl class loader.
157164
*
@@ -208,26 +215,68 @@ private static URI rootURI(URL url) {
208215

209216
private static Map<String, CodeSource> getProviderPrefixes(ClassLoader parent, String providerName) {
210217
String providerPrefix = IMPL_PREFIX + providerName;
211-
URL manifest = parent.getResource(providerPrefix + MANIFEST_FILE);
212-
if (manifest == null) {
218+
URL listingURL = parent.getResource(providerPrefix + JAR_LISTING_FILE);
219+
if (listingURL == null) {
213220
throw new IllegalStateException("missing x-content provider jars list");
214221
}
215222
try (
216-
InputStream in = manifest.openStream();
223+
InputStream in = listingURL.openStream();
217224
InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
218225
BufferedReader reader = new BufferedReader(isr)
219226
) {
220227
List<String> jars = reader.lines().toList();
221-
Map<String, CodeSource> map = new HashMap<>();
228+
Map<String, CodeSource> map = new LinkedHashMap<>();
222229
for (String jar : jars) {
223-
map.put(providerPrefix + "/" + jar, new CodeSource(new URL(manifest, jar), (CodeSigner[]) null /*signers*/));
230+
String jarPrefix = providerPrefix + "/" + jar;
231+
if (isMultiRelease(parent, jarPrefix)) {
232+
List<String> versionPrefixes = getVersionPrefixes(parent, jarPrefix);
233+
for (String versionPrefix : versionPrefixes) {
234+
map.put(versionPrefix, codeSource(listingURL, jar));
235+
}
236+
}
237+
// TODO: LinkedHashMap, verify order, need to preserve order for versioned entries
238+
map.put(jarPrefix, codeSource(listingURL, jar));
224239
}
225240
return Collections.unmodifiableMap(map);
226241
} catch (IOException e) {
227242
throw new UncheckedIOException(e);
228243
}
229244
}
230245

246+
private static CodeSource codeSource(URL baseURL, String jarName) throws MalformedURLException {
247+
return new CodeSource(new URL(baseURL, jarName), (CodeSigner[]) null /*signers*/);
248+
}
249+
250+
private static boolean isMultiRelease(ClassLoader parent, String jarPrefix) throws IOException {
251+
try (InputStream is = parent.getResourceAsStream(jarPrefix + "/META-INF/MANIFEST.MF")) {
252+
if (is != null) {
253+
Manifest manifest = new Manifest(is);
254+
return Boolean.parseBoolean(manifest.getMainAttributes().getValue(MULTI_RELEASE));
255+
}
256+
}
257+
return false;
258+
}
259+
260+
private static final int BASE_VERSION_FEATURE = 8; // lowest supported release version
261+
private static final int RUNTIME_VERSION_FEATURE = Runtime.version().feature();
262+
263+
static {
264+
assert RUNTIME_VERSION_FEATURE >= BASE_VERSION_FEATURE;
265+
}
266+
267+
private static final String MRJAR_VERSION_PREFIX = "META-INF/versions/";
268+
269+
private static List<String> getVersionPrefixes(ClassLoader parent, String jarPrefix) throws IOException {
270+
List<String> versions = new ArrayList<>();
271+
for (int v = RUNTIME_VERSION_FEATURE; v >= BASE_VERSION_FEATURE; v--) {
272+
URL url = parent.getResource(jarPrefix + "/" + MRJAR_VERSION_PREFIX + v);
273+
if (url != null) {
274+
versions.add(jarPrefix + "/" + MRJAR_VERSION_PREFIX + v);
275+
}
276+
}
277+
return versions;
278+
}
279+
231280
private static String getParent(String uriString) {
232281
int index = uriString.lastIndexOf('/');
233282
if (index > 0) {

0 commit comments

Comments
 (0)