Skip to content

Commit 5e929ef

Browse files
authored
Merge pull request quarkusio#50234 from gsmet/avoid-toexternalform
Reduce allocations due to ClassLoader#defineClassSourceLocation
2 parents f16dd61 + 9882fdc commit 5e929ef

File tree

3 files changed

+102
-7
lines changed

3 files changed

+102
-7
lines changed

independent-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/JarResource.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
package io.quarkus.bootstrap.runner;
22

3+
import java.io.File;
4+
import java.io.FileInputStream;
5+
import java.io.FileNotFoundException;
36
import java.io.IOException;
47
import java.io.InputStream;
58
import java.net.MalformedURLException;
69
import java.net.URI;
710
import java.net.URISyntaxException;
811
import java.net.URL;
12+
import java.net.URLConnection;
13+
import java.net.URLStreamHandler;
914
import java.nio.file.Path;
1015
import java.security.CodeSource;
1116
import java.security.ProtectionDomain;
@@ -44,7 +49,7 @@ public void init() {
4449
path = '/' + path;
4550
}
4651
URI uri = new URI("file", null, path, null);
47-
url = uri.toURL();
52+
url = new URL((URL) null, uri.toString(), new JarUrlStreamHandler(uri));
4853
} catch (URISyntaxException | MalformedURLException e) {
4954
throw new RuntimeException("Unable to create protection domain for " + jarPath, e);
5055
}
@@ -201,4 +206,73 @@ public boolean equals(Object o) {
201206
public int hashCode() {
202207
return Objects.hashCode(jarPath);
203208
}
209+
210+
/**
211+
* This URLStreamHandler is designed to handle only one jar, which is the one passed in the constructor.
212+
* The goal here is to cache the external form of the URL.
213+
* <p>
214+
* Do not use this class outside of this extremely specific purpose.
215+
*/
216+
private static class JarUrlStreamHandler extends URLStreamHandler {
217+
218+
private final String externalForm;
219+
220+
private JarUrlStreamHandler(URI uri) {
221+
this.externalForm = "file:".concat(uri.getRawPath());
222+
// while it would be more optimized to store the URI here for when we open connections
223+
// opening a connection for ProtectionDomains is actually extremely rare
224+
// and never done in production at runtime so we favored reducing memory allocations for the common case
225+
}
226+
227+
@Override
228+
protected URLConnection openConnection(URL u) throws IOException {
229+
return new JarURLConnection(u);
230+
}
231+
232+
@Override
233+
protected String toExternalForm(URL u) {
234+
return externalForm;
235+
}
236+
}
237+
238+
private static class JarURLConnection extends URLConnection {
239+
240+
private final File file;
241+
242+
private JarURLConnection(URL url) throws IOException {
243+
super(url);
244+
try {
245+
this.file = new File(url.toURI());
246+
} catch (URISyntaxException e) {
247+
throw new IOException(e);
248+
}
249+
}
250+
251+
@Override
252+
public void connect() throws IOException {
253+
if (!file.exists()) {
254+
throw new FileNotFoundException(file.getAbsolutePath());
255+
}
256+
}
257+
258+
@Override
259+
public InputStream getInputStream() throws IOException {
260+
return new FileInputStream(file);
261+
}
262+
263+
@Override
264+
public int getContentLength() {
265+
return (int) file.length();
266+
}
267+
268+
@Override
269+
public long getContentLengthLong() {
270+
return file.length();
271+
}
272+
273+
@Override
274+
public String getContentType() {
275+
return "application/java-archive";
276+
}
277+
}
204278
}

integration-tests/maven/src/test/resources-filtered/projects/classic/src/main/java/org/acme/ProtectionDomain.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import jakarta.ws.rs.Path;
77
import java.io.IOException;
88
import java.io.InputStream;
9+
import java.io.PrintWriter;
10+
import java.io.StringWriter;
911
import java.net.URL;
12+
import java.util.Arrays;
1013
import java.util.jar.JarInputStream;
1114
import java.util.jar.Manifest;
1215
import java.nio.charset.StandardCharsets;
@@ -46,8 +49,9 @@ private String runAssertions(Supplier<String>... assertions) {
4649

4750
private String assertReadManifestFromJar() {
4851
final String testType = "manifest-from-jar";
52+
URL location = null;
4953
try {
50-
URL location = org.apache.commons.io.Charsets.class.getProtectionDomain().getCodeSource().getLocation();
54+
location = org.apache.commons.io.Charsets.class.getProtectionDomain().getCodeSource().getLocation();
5155
if (location == null) {
5256
return errorResult(testType, "location should not be null");
5357
}
@@ -66,12 +70,19 @@ private String assertReadManifestFromJar() {
6670
}
6771
return SUCCESS;
6872
} catch (Exception e) {
69-
e.printStackTrace();
70-
return errorResult(testType, "exception during resolution of resource");
73+
return errorResult(testType, "Exception during resolution of resource (" + location + "): " + e + "\n" + getStackTraceAsString(e));
7174
}
7275
}
7376

7477
private String errorResult(String testType, String message) {
7578
return testType + " / " + message;
7679
}
80+
81+
private static String getStackTraceAsString(Throwable t) {
82+
StringWriter sw = new StringWriter();
83+
try (PrintWriter pw = new PrintWriter(sw)) {
84+
t.printStackTrace(pw);
85+
}
86+
return sw.toString();
87+
}
7788
}

integration-tests/maven/src/test/resources-filtered/projects/rr-with-json-logging/src/main/java/org/acme/ProtectionDomain.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import jakarta.ws.rs.Path;
77
import java.io.IOException;
88
import java.io.InputStream;
9+
import java.io.PrintWriter;
10+
import java.io.StringWriter;
911
import java.net.URL;
1012
import java.util.jar.JarInputStream;
1113
import java.util.jar.Manifest;
@@ -46,8 +48,9 @@ private String runAssertions(Supplier<String>... assertions) {
4648

4749
private String assertReadManifestFromJar() {
4850
final String testType = "manifest-from-jar";
51+
URL location = null;
4952
try {
50-
URL location = org.apache.commons.io.Charsets.class.getProtectionDomain().getCodeSource().getLocation();
53+
location = org.apache.commons.io.Charsets.class.getProtectionDomain().getCodeSource().getLocation();
5154
if (location == null) {
5255
return errorResult(testType, "location should not be null");
5356
}
@@ -66,12 +69,19 @@ private String assertReadManifestFromJar() {
6669
}
6770
return SUCCESS;
6871
} catch (Exception e) {
69-
e.printStackTrace();
70-
return errorResult(testType, "exception during resolution of resource");
72+
return errorResult(testType, "Exception during resolution of resource (" + location + "): " + e + "\n" + getStackTraceAsString(e));
7173
}
7274
}
7375

7476
private String errorResult(String testType, String message) {
7577
return testType + " / " + message;
7678
}
79+
80+
private static String getStackTraceAsString(Throwable t) {
81+
StringWriter sw = new StringWriter();
82+
try (PrintWriter pw = new PrintWriter(sw)) {
83+
t.printStackTrace(pw);
84+
}
85+
return sw.toString();
86+
}
7787
}

0 commit comments

Comments
 (0)