mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:30:28 +00:00
Close class resource InputStream in ThrowawayClassLoader
Prior to this commit, ThrowawayClassLoader#loadClassFromResource opened an InputStream via getResourceAsStream(...) but never closed it. The stream leaked on both the success path (after defineClass) and the IOException path, as the surrounding try-block had neither a finally nor a try-with-resources clause. This commit adapts the existing inputStream variable as a try-with-resources resource so that it is closed on every path, leaving the loading logic unchanged. Closes gh-36933 Signed-off-by: junhyeong9812 <pickjog@gmail.com>
This commit is contained in:
+1
-2
@@ -65,12 +65,11 @@ class ThrowawayClassLoader extends ClassLoader {
|
||||
if (inputStream == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
try (inputStream) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
inputStream.transferTo(outputStream);
|
||||
byte[] bytes = outputStream.toByteArray();
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex);
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2002-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.aot.nativex.feature;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ThrowawayClassLoader}.
|
||||
*
|
||||
* @author Junhyeong Kim
|
||||
*/
|
||||
class ThrowawayClassLoaderTests {
|
||||
|
||||
@Test
|
||||
void loadingClassFromResourceClosesInputStream() throws Exception {
|
||||
String className = Probe.class.getName();
|
||||
byte[] classBytes = classBytesOf(className);
|
||||
AtomicBoolean closed = new AtomicBoolean();
|
||||
|
||||
// The grandparent resolves bootstrap classes only, so super.loadClass(...)
|
||||
// fails for the probe class and ThrowawayClassLoader falls back to loading it
|
||||
// from the resource stream provided below.
|
||||
ClassLoader resourceLoader = new ClassLoader(new ClassLoader(null) {}) {
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
return new TrackingInputStream(new ByteArrayInputStream(classBytes), closed);
|
||||
}
|
||||
};
|
||||
|
||||
ThrowawayClassLoader classLoader = new ThrowawayClassLoader(resourceLoader);
|
||||
Class<?> loaded = classLoader.loadClass(className);
|
||||
|
||||
assertThat(loaded.getName()).isEqualTo(className);
|
||||
assertThat(closed).as("InputStream closed").isTrue();
|
||||
}
|
||||
|
||||
private static byte[] classBytesOf(String className) throws IOException {
|
||||
String resourceName = className.replace('.', '/') + ".class";
|
||||
try (InputStream in = ThrowawayClassLoaderTests.class.getClassLoader().getResourceAsStream(resourceName)) {
|
||||
assertThat(in).as("class bytes for %s", className).isNotNull();
|
||||
return in.readAllBytes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class Probe {
|
||||
}
|
||||
|
||||
|
||||
private static final class TrackingInputStream extends FilterInputStream {
|
||||
|
||||
private final AtomicBoolean closed;
|
||||
|
||||
TrackingInputStream(InputStream in, AtomicBoolean closed) {
|
||||
super(in);
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.closed.set(true);
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user