Skip to content

Commit 681a866

Browse files
committed
Exclude Java agents from class loader created by PropertiesLauncher
PropertiesLauncher creates a ClassLoader that is used by the Launcher to load an application’s classes. During the creation of this ClassLoader URLs from its ClassLoader. This resulted resulting in Java agents that are added to the system class loader via the -javaagent launch option being available on both the system class loader and the created class loader. Java agents are intended to always be loaded by the system class loader. Making them available on another class loader breaks this model. This is the same problem that was in ExecutableArchiveLauncher and that was fixed in ee08667 (see spring-projectsgh-863). This commit updates PropertiesLauncher so that it skips the URLs of any Java agents (found by examining the JVM’s input arguments) when copying URLs over to the new ClassLoader, thereby ensuring that Java agents are only ever loaded by the system class loader. Closes spring-projectsgh-4911
1 parent b997392 commit 681a866

File tree

2 files changed

+63
-15
lines changed

2 files changed

+63
-15
lines changed

spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/PropertiesLauncher.java

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -72,6 +72,7 @@
7272
*
7373
* @author Dave Syer
7474
* @author Janne Valkealahti
75+
* @author Andy Wilkinson
7576
*/
7677
public class PropertiesLauncher extends Launcher {
7778

@@ -132,18 +133,25 @@ public class PropertiesLauncher extends Launcher {
132133

133134
private final File home;
134135

136+
private final JavaAgentDetector javaAgentDetector;
137+
135138
private List<String> paths = new ArrayList<String>(DEFAULT_PATHS);
136139

137140
private final Properties properties = new Properties();
138141

139142
private Archive parent;
140143

141144
public PropertiesLauncher() {
145+
this(new InputArgumentsJavaAgentDetector());
146+
}
147+
148+
PropertiesLauncher(JavaAgentDetector javaAgentDetector) {
142149
if (!isDebug()) {
143150
this.logger.setLevel(Level.SEVERE);
144151
}
145152
try {
146153
this.home = getHomeDirectory();
154+
this.javaAgentDetector = javaAgentDetector;
147155
initializeProperties(this.home);
148156
initializePaths();
149157
this.parent = createArchive();
@@ -517,21 +525,12 @@ private void addParentClassLoaderEntries(List<Archive> lib)
517525
ClassLoader parentClassLoader = getClass().getClassLoader();
518526
List<Archive> urls = new ArrayList<Archive>();
519527
for (URL url : getURLs(parentClassLoader)) {
520-
if (url.toString().endsWith(".jar") || url.toString().endsWith(".zip")) {
521-
urls.add(new JarFileArchive(new File(url.toURI())));
522-
}
523-
else if (url.toString().endsWith("/*")) {
524-
String name = url.getFile();
525-
File dir = new File(name.substring(0, name.length() - 1));
526-
if (dir.exists()) {
527-
urls.add(new ExplodedArchive(
528-
new File(name.substring(0, name.length() - 1)), false));
528+
if (!this.javaAgentDetector.isJavaAgentJar(url)) {
529+
Archive archive = createArchiveIfPossible(url);
530+
if (archive != null) {
531+
urls.add(archive);
529532
}
530533
}
531-
else {
532-
String filename = URLDecoder.decode(url.getFile(), "UTF-8");
533-
urls.add(new ExplodedArchive(new File(filename)));
534-
}
535534
}
536535
// The parent archive might have a "lib/" directory, meaning we are running from
537536
// an executable JAR. We add nested entries from there with low priority (i.e. at
@@ -545,6 +544,26 @@ else if (url.toString().endsWith("/*")) {
545544
}
546545
}
547546

547+
private Archive createArchiveIfPossible(URL url)
548+
throws IOException, URISyntaxException {
549+
if (url.toString().endsWith(".jar") || url.toString().endsWith(".zip")) {
550+
return new JarFileArchive(new File(url.toURI()));
551+
}
552+
else if (url.toString().endsWith("/*")) {
553+
String name = url.getFile();
554+
File dir = new File(name.substring(0, name.length() - 1));
555+
if (dir.exists()) {
556+
return new ExplodedArchive(new File(name.substring(0, name.length() - 1)),
557+
false);
558+
}
559+
}
560+
else {
561+
String filename = URLDecoder.decode(url.getFile(), "UTF-8");
562+
return new ExplodedArchive(new File(filename));
563+
}
564+
return null;
565+
}
566+
548567
private void addNestedArchivesFromParent(List<Archive> urls) {
549568
int index = findArchive(urls, this.parent);
550569
if (index >= 0) {

spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/PropertiesLauncherTests.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2013 the original author or authors.
2+
* Copyright 2012-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,32 +22,45 @@
2222
import java.net.URLClassLoader;
2323
import java.util.Arrays;
2424
import java.util.Collections;
25+
import java.util.List;
2526

2627
import org.junit.After;
2728
import org.junit.Before;
2829
import org.junit.Rule;
2930
import org.junit.Test;
31+
import org.mockito.Mock;
32+
import org.mockito.MockitoAnnotations;
3033

3134
import org.springframework.boot.loader.archive.Archive;
3235
import org.springframework.test.util.ReflectionTestUtils;
3336

37+
import static org.hamcrest.Matchers.equalTo;
38+
import static org.hamcrest.Matchers.is;
3439
import static org.junit.Assert.assertEquals;
3540
import static org.junit.Assert.assertNotNull;
3641
import static org.junit.Assert.assertNull;
42+
import static org.junit.Assert.assertThat;
3743
import static org.junit.Assert.assertTrue;
44+
import static org.mockito.BDDMockito.given;
45+
import static org.mockito.Mockito.verify;
3846

3947
/**
4048
* Tests for {@link PropertiesLauncher}.
4149
*
4250
* @author Dave Syer
51+
* @author Andy Wilkinson
4352
*/
4453
public class PropertiesLauncherTests {
4554

55+
@Mock
56+
private JavaAgentDetector javaAgentDetector;
57+
4658
@Rule
4759
public OutputCapture output = new OutputCapture();
4860

4961
@Before
5062
public void setup() throws IOException {
63+
MockitoAnnotations.initMocks(this);
5164
System.setProperty("loader.home",
5265
new File("src/test/resources").getAbsolutePath());
5366
}
@@ -175,6 +188,22 @@ public void testArgsEnhanced() throws Exception {
175188
assertEquals("[foo, bar]", Arrays.asList(launcher.getArgs("bar")).toString());
176189
}
177190

191+
@Test
192+
public void testJavaAgentJarsAreExcludedFromClasspath() throws Exception {
193+
List<Archive> allArchives = new PropertiesLauncher().getClassPathArchives();
194+
URL[] parentUrls = ((URLClassLoader) getClass().getClassLoader()).getURLs();
195+
for (URL url : parentUrls) {
196+
given(this.javaAgentDetector.isJavaAgentJar(url)).willReturn(true);
197+
}
198+
List<Archive> nonAgentArchives = new PropertiesLauncher(this.javaAgentDetector)
199+
.getClassPathArchives();
200+
assertThat(nonAgentArchives.size(),
201+
is(equalTo(allArchives.size() - parentUrls.length)));
202+
for (URL url : parentUrls) {
203+
verify(this.javaAgentDetector).isJavaAgentJar(url);
204+
}
205+
}
206+
178207
private void waitFor(String value) throws Exception {
179208
int count = 0;
180209
boolean timeout = false;

0 commit comments

Comments
 (0)