1818
1919import static org .junit .Assert .*;
2020
21+ import java .io .ByteArrayOutputStream ;
22+ import java .io .IOException ;
23+ import java .io .InputStream ;
24+ import java .lang .reflect .Method ;
25+ import java .net .URL ;
26+ import java .net .URLDecoder ;
27+ import java .net .URLConnection ;
28+ import java .net .URLStreamHandler ;
29+ import java .nio .file .Files ;
30+ import java .nio .file .Path ;
2131import org .junit .Test ;
2232
2333public class ClassLoaderUtilTest {
2434 private static boolean shouldFailInInitialization = false ;
35+
2536 @ Test
2637 public void testGetClassLoader () {
2738 assertNotNull (ClassLoaderUtil .getLoader ());
2839 }
2940
41+ @ Test
42+ public void testResolveClassPathWithFileUrl () throws Exception {
43+ Path tempDir = Files .createTempDirectory ("apollo class path" );
44+ try {
45+ ClassLoader classLoader = classLoaderReturning (tempDir .toUri ().toURL ());
46+
47+ assertEquals (URLDecoder .decode (tempDir .toUri ().toURL ().getPath (), "utf-8" ),
48+ ClassLoaderUtil .resolveClassPath (classLoader , "fallback" ));
49+ } finally {
50+ Files .deleteIfExists (tempDir );
51+ }
52+ }
53+
54+ @ Test
55+ public void testResolveClassPathFallsBackForNestedJarUrl () throws Exception {
56+ String fallback = "/tmp/fallback" ;
57+ URL nestedJarUrl = createUrl ("jar:nested:/tmp/apollo-app.jar/!BOOT-INF/classes/!/" );
58+ ClassLoader classLoader = classLoaderReturning (nestedJarUrl );
59+
60+ assertEquals (fallback , ClassLoaderUtil .resolveClassPath (classLoader , fallback ));
61+ }
62+
63+ @ Test
64+ public void testResolveClassPathPreservesWindowsStyleFileUrlFormat () throws Exception {
65+ URL windowsFileUrl = new URL ("file:/C:/Program%20Files/apollo/classes/" );
66+ ClassLoader classLoader = classLoaderReturning (windowsFileUrl );
67+
68+ assertEquals ("/C:/Program Files/apollo/classes/" ,
69+ ClassLoaderUtil .resolveClassPath (classLoader , "fallback" ));
70+ }
71+
72+ @ Test
73+ public void testGetClassPathFallsBackToUserDirForNestedJarUrl () throws Exception {
74+ String expectedClassPath = System .getProperty ("user.dir" );
75+ ClassLoader contextClassLoader =
76+ classLoaderReturning (createUrl ("jar:nested:/tmp/apollo-app.jar/!BOOT-INF/classes/!/" ));
77+
78+ assertEquals (expectedClassPath , isolatedClassPath (contextClassLoader ));
79+ }
80+
81+ @ Test
82+ public void testGetClassPathFallsBackToUserDirWhenLookupFails () throws Exception {
83+ String expectedClassPath = System .getProperty ("user.dir" );
84+ ClassLoader contextClassLoader = new ClassLoader (null ) {
85+ @ Override
86+ public URL getResource (String name ) {
87+ throw new RuntimeException ("lookup failed" );
88+ }
89+ };
90+
91+ assertEquals (expectedClassPath , isolatedClassPath (contextClassLoader ));
92+ }
93+
3094 @ Test
3195 public void testIsClassPresent () {
3296 assertTrue (ClassLoaderUtil .isClassPresent ("java.lang.String" ));
@@ -50,4 +114,76 @@ public static class ClassWithInitializationError {
50114 }
51115 }
52116 }
53- }
117+
118+ private ClassLoader classLoaderReturning (URL resource ) {
119+ return new ClassLoader (null ) {
120+ @ Override
121+ public URL getResource (String name ) {
122+ return resource ;
123+ }
124+ };
125+ }
126+
127+ private URL createUrl (String spec ) throws Exception {
128+ return new URL (null , spec , new URLStreamHandler () {
129+ @ Override
130+ protected URLConnection openConnection (URL url ) {
131+ throw new UnsupportedOperationException ();
132+ }
133+ });
134+ }
135+
136+ private String isolatedClassPath (ClassLoader contextClassLoader ) throws Exception {
137+ ClassLoader originalClassLoader = Thread .currentThread ().getContextClassLoader ();
138+ try {
139+ Thread .currentThread ().setContextClassLoader (contextClassLoader );
140+ Class <?> isolatedClassLoaderUtil = newIsolatedClassLoader ().loadClass (
141+ ClassLoaderUtil .class .getName ());
142+ Method getClassPath = isolatedClassLoaderUtil .getMethod ("getClassPath" );
143+ return (String ) getClassPath .invoke (null );
144+ } finally {
145+ Thread .currentThread ().setContextClassLoader (originalClassLoader );
146+ }
147+ }
148+
149+ private ClassLoader newIsolatedClassLoader () throws IOException {
150+ String className = ClassLoaderUtil .class .getName ();
151+ String classFile = className .replace ('.' , '/' ) + ".class" ;
152+ byte [] classBytes = readClassBytes (classFile );
153+
154+ return new ClassLoader (ClassLoaderUtil .class .getClassLoader ()) {
155+ @ Override
156+ protected Class <?> loadClass (String name , boolean resolve ) throws ClassNotFoundException {
157+ if (!className .equals (name )) {
158+ return super .loadClass (name , resolve );
159+ }
160+
161+ synchronized (getClassLoadingLock (name )) {
162+ Class <?> loadedClass = findLoadedClass (name );
163+ if (loadedClass == null ) {
164+ loadedClass = defineClass (name , classBytes , 0 , classBytes .length );
165+ }
166+ if (resolve ) {
167+ resolveClass (loadedClass );
168+ }
169+ return loadedClass ;
170+ }
171+ }
172+ };
173+ }
174+
175+ private byte [] readClassBytes (String classFile ) throws IOException {
176+ try (InputStream inputStream = ClassLoaderUtil .class .getClassLoader ().getResourceAsStream (
177+ classFile );
178+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream ()) {
179+ assertNotNull (inputStream );
180+
181+ byte [] buffer = new byte [1024 ];
182+ int bytesRead ;
183+ while ((bytesRead = inputStream .read (buffer )) != -1 ) {
184+ outputStream .write (buffer , 0 , bytesRead );
185+ }
186+ return outputStream .toByteArray ();
187+ }
188+ }
189+ }
0 commit comments