Skip to content

Commit 1828bd8

Browse files
committed
fix: handle nested jar class path fallback
Fixes apolloconfig/apollo#5592
1 parent c89320a commit 1828bd8

File tree

4 files changed

+109
-18
lines changed

4 files changed

+109
-18
lines changed

CHANGES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Apollo Java 2.6.0
66

77
------------------
88

9-
*
9+
* [Fix Apollo client local cache fallback for Spring Boot 3 executable JARs](https://github.com/apolloconfig/apollo-java/pull/136)
1010

1111
------------------
1212
All issues and pull requests are [here](https://github.com/apolloconfig/apollo-java/milestone/6?closed=1)

apollo-client-config-data/src/test/java/com/ctrip/framework/apollo/config/data/integration/ConfigDataIntegrationTest.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@
3232
import com.ctrip.framework.apollo.spi.ConfigFactoryManager;
3333
import com.ctrip.framework.apollo.spi.ConfigRegistry;
3434
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
35+
import com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer;
3536
import com.google.common.collect.Table;
3637
import java.lang.reflect.Field;
3738
import java.lang.reflect.Method;
39+
import java.util.HashMap;
3840
import java.util.Map;
3941
import java.util.concurrent.ArrayBlockingQueue;
4042
import java.util.concurrent.BlockingQueue;
@@ -80,13 +82,14 @@ public class ConfigDataIntegrationTest {
8082
private static final EmbeddedApollo embeddedApollo = new EmbeddedApollo();
8183

8284
private static final ExternalResource apolloStateResource = new ExternalResource() {
83-
private String originalAppId;
8485
private String originalEnv;
86+
private Map<String, String> originalApolloSystemProperties = new HashMap<>();
8587

8688
@Override
8789
protected void before() throws Throwable {
88-
originalAppId = System.getProperty("app.id");
90+
originalApolloSystemProperties = snapshotApolloSystemProperties();
8991
originalEnv = System.getProperty("env");
92+
clearApolloSystemProperties();
9093
System.setProperty("app.id", TEST_APP_ID);
9194
System.setProperty("env", TEST_ENV);
9295
resetApolloStaticState();
@@ -99,7 +102,7 @@ protected void after() {
99102
} catch (Exception ex) {
100103
throw new RuntimeException(ex);
101104
} finally {
102-
restoreOrClear("app.id", originalAppId);
105+
restoreApolloSystemProperties(originalApolloSystemProperties);
103106
restoreOrClear("env", originalEnv);
104107
}
105108
}
@@ -118,6 +121,9 @@ public void beforeEach() {
118121
@After
119122
public void afterEach() throws Exception {
120123
resetApolloStaticState();
124+
clearApolloSystemProperties();
125+
System.setProperty("app.id", TEST_APP_ID);
126+
System.setProperty("env", TEST_ENV);
121127
}
122128

123129
@Autowired
@@ -283,6 +289,27 @@ private static void restoreOrClear(String key, String originalValue) {
283289
System.setProperty(key, originalValue);
284290
}
285291

292+
private static Map<String, String> snapshotApolloSystemProperties() {
293+
Map<String, String> originalProperties = new HashMap<>();
294+
for (String propertyName : ApolloApplicationContextInitializer.APOLLO_SYSTEM_PROPERTIES) {
295+
originalProperties.put(propertyName, System.getProperty(propertyName));
296+
}
297+
return originalProperties;
298+
}
299+
300+
private static void clearApolloSystemProperties() {
301+
for (String propertyName : ApolloApplicationContextInitializer.APOLLO_SYSTEM_PROPERTIES) {
302+
System.clearProperty(propertyName);
303+
}
304+
}
305+
306+
private static void restoreApolloSystemProperties(Map<String, String> originalProperties) {
307+
clearApolloSystemProperties();
308+
for (Map.Entry<String, String> entry : originalProperties.entrySet()) {
309+
restoreOrClear(entry.getKey(), entry.getValue());
310+
}
311+
}
312+
286313
private static void addOrModifyForAllAppIds(String namespace, String key, String value) {
287314
embeddedApollo.addOrModifyProperty(TEST_APP_ID, namespace, key, value);
288315
embeddedApollo.addOrModifyProperty(

apollo-core/src/main/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtil.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
package com.ctrip.framework.apollo.core.utils;
1818

1919
import com.google.common.base.Strings;
20-
2120
import org.slf4j.Logger;
2221
import org.slf4j.LoggerFactory;
23-
2422
import java.net.URL;
2523
import java.net.URLDecoder;
2624

@@ -40,19 +38,12 @@ public class ClassLoaderUtil {
4038
}
4139

4240
try {
43-
URL url = loader.getResource("");
44-
// get class path
45-
if (url != null) {
46-
classPath = url.getPath();
47-
classPath = URLDecoder.decode(classPath, "utf-8");
48-
}
49-
50-
// 如果是jar包内的,则返回当前路径
51-
if (Strings.isNullOrEmpty(classPath) || classPath.contains(".jar!")) {
52-
classPath = System.getProperty("user.dir");
41+
classPath = resolveClassPath(loader, null);
42+
if (Strings.isNullOrEmpty(classPath)) {
43+
classPath = getDefaultClassPath();
5344
}
5445
} catch (Throwable ex) {
55-
classPath = System.getProperty("user.dir");
46+
classPath = getDefaultClassPath();
5647
logger.warn("Failed to locate class path, fallback to user.dir: {}", classPath, ex);
5748
}
5849
}
@@ -65,6 +56,23 @@ public static String getClassPath() {
6556
return classPath;
6657
}
6758

59+
static String resolveClassPath(ClassLoader classLoader, String defaultClassPath) throws Exception {
60+
URL url = classLoader.getResource("");
61+
if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) {
62+
return defaultClassPath;
63+
}
64+
65+
String resolvedClassPath = URLDecoder.decode(url.getPath(), "utf-8");
66+
if (Strings.isNullOrEmpty(resolvedClassPath)) {
67+
return defaultClassPath;
68+
}
69+
return resolvedClassPath;
70+
}
71+
72+
private static String getDefaultClassPath() {
73+
return System.getProperty("user.dir");
74+
}
75+
6876
public static boolean isClassPresent(String className) {
6977
try {
7078
Class.forName(className);

apollo-core/src/test/java/com/ctrip/framework/apollo/core/utils/ClassLoaderUtilTest.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,53 @@
1818

1919
import static org.junit.Assert.*;
2020

21+
import java.net.URL;
22+
import java.net.URLDecoder;
23+
import java.net.URLConnection;
24+
import java.net.URLStreamHandler;
25+
import java.nio.file.Files;
26+
import java.nio.file.Path;
2127
import org.junit.Test;
2228

2329
public class ClassLoaderUtilTest {
2430
private static boolean shouldFailInInitialization = false;
31+
2532
@Test
2633
public void testGetClassLoader() {
2734
assertNotNull(ClassLoaderUtil.getLoader());
2835
}
2936

37+
@Test
38+
public void testResolveClassPathWithFileUrl() throws Exception {
39+
Path tempDir = Files.createTempDirectory("apollo class path");
40+
try {
41+
ClassLoader classLoader = classLoaderReturning(tempDir.toUri().toURL());
42+
43+
assertEquals(URLDecoder.decode(tempDir.toUri().toURL().getPath(), "utf-8"),
44+
ClassLoaderUtil.resolveClassPath(classLoader, "fallback"));
45+
} finally {
46+
Files.deleteIfExists(tempDir);
47+
}
48+
}
49+
50+
@Test
51+
public void testResolveClassPathFallsBackForNestedJarUrl() throws Exception {
52+
String fallback = "/tmp/fallback";
53+
URL nestedJarUrl = createUrl("jar:nested:/tmp/apollo-app.jar/!BOOT-INF/classes/!/");
54+
ClassLoader classLoader = classLoaderReturning(nestedJarUrl);
55+
56+
assertEquals(fallback, ClassLoaderUtil.resolveClassPath(classLoader, fallback));
57+
}
58+
59+
@Test
60+
public void testResolveClassPathPreservesWindowsStyleFileUrlFormat() throws Exception {
61+
URL windowsFileUrl = new URL("file:/C:/Program%20Files/apollo/classes/");
62+
ClassLoader classLoader = classLoaderReturning(windowsFileUrl);
63+
64+
assertEquals("/C:/Program Files/apollo/classes/",
65+
ClassLoaderUtil.resolveClassPath(classLoader, "fallback"));
66+
}
67+
3068
@Test
3169
public void testIsClassPresent() {
3270
assertTrue(ClassLoaderUtil.isClassPresent("java.lang.String"));
@@ -50,4 +88,22 @@ public static class ClassWithInitializationError {
5088
}
5189
}
5290
}
53-
}
91+
92+
private ClassLoader classLoaderReturning(URL resource) {
93+
return new ClassLoader(null) {
94+
@Override
95+
public URL getResource(String name) {
96+
return resource;
97+
}
98+
};
99+
}
100+
101+
private URL createUrl(String spec) throws Exception {
102+
return new URL(null, spec, new URLStreamHandler() {
103+
@Override
104+
protected URLConnection openConnection(URL url) {
105+
throw new UnsupportedOperationException();
106+
}
107+
});
108+
}
109+
}

0 commit comments

Comments
 (0)