Skip to content

Commit 835b39e

Browse files
committed
tests: improve TestObjects.fill(...) to support interface params that have builder implementations
- For example, v3.GetApplicationResponse.Builder had a nested param of type LifecycleData. That's a method-less interface, with multiple possible implementations. Each implementation has a builder. We now pick whatever implementation we find, fill its builder, and use that in the root object.
1 parent 6f43cf0 commit 835b39e

File tree

3 files changed

+133
-89
lines changed

3 files changed

+133
-89
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.cloudfoundry.operations;
2+
3+
import java.io.File;
4+
import java.lang.reflect.Modifier;
5+
import java.net.URL;
6+
import java.util.Arrays;
7+
import java.util.Collections;
8+
import java.util.Enumeration;
9+
import java.util.List;
10+
import java.util.stream.Collectors;
11+
import java.util.stream.Stream;
12+
13+
class ReflectionUtils {
14+
15+
private ReflectionUtils() { // do not instantiate this class
16+
}
17+
18+
/**
19+
* Find implementations for a given interface type. Uses reflection.
20+
*/
21+
public static <T> List<Class<? extends T>> findImplementations(Class<T> interfaceType) {
22+
try {
23+
// Get the class loader
24+
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
25+
26+
// Get the package path
27+
String path = interfaceType.getPackage().getName().replace('.', '/');
28+
Enumeration<URL> resources = classLoader.getResources(path);
29+
return Collections.list(resources).stream()
30+
.map(URL::getFile)
31+
.map(File::new)
32+
.filter(File::exists)
33+
.flatMap(
34+
directory ->
35+
scanDirectory(
36+
directory,
37+
interfaceType.getPackage().getName(),
38+
interfaceType))
39+
.collect(Collectors.toList());
40+
} catch (Exception ignored) {
41+
42+
}
43+
return Collections.emptyList();
44+
}
45+
46+
private static <T> Stream<Class<? extends T>> scanDirectory(
47+
File directory, String packageName, Class<T> interfaceType) {
48+
File[] files = directory.listFiles();
49+
if (files == null) {
50+
return Stream.empty();
51+
}
52+
53+
Stream<Class<? extends T>> classes =
54+
Arrays.stream(files)
55+
.filter(fileName -> fileName.getName().endsWith(".class"))
56+
.map(
57+
fileName ->
58+
packageName
59+
+ '.'
60+
+ fileName.getName().replaceAll("\\.class$", ""))
61+
.map(
62+
className -> {
63+
try {
64+
Class<?> clazz = Class.forName(className);
65+
if (interfaceType.isAssignableFrom(clazz)
66+
&& !clazz.isInterface()
67+
&& !Modifier.isAbstract(clazz.getModifiers())) {
68+
Class<? extends T> subclass =
69+
clazz.asSubclass(interfaceType);
70+
return subclass;
71+
}
72+
} catch (ClassNotFoundException ignored) {
73+
}
74+
return interfaceType;
75+
})
76+
.filter(clazz -> clazz != interfaceType);
77+
Stream<Class<? extends T>> directories =
78+
Arrays.stream(files)
79+
.filter(File::isDirectory)
80+
.flatMap(
81+
fileName ->
82+
scanDirectory(
83+
fileName,
84+
packageName + "." + fileName.getName(),
85+
interfaceType));
86+
return Stream.concat(classes, directories);
87+
}
88+
}

cloudfoundry-operations/src/test/java/org/cloudfoundry/operations/TestObjects.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,12 @@ private static <T> T fill(T builder, Optional<String> modifier) {
109109
return getConfigurationMethods(builderType, builderMethods, builtGetters).stream()
110110
.collect(
111111
() -> builder,
112-
(b, method) ->
113-
ReflectionUtils.invokeMethod(
114-
method, b, getConfiguredValue(method, modifier)),
112+
(b, method) -> {
113+
Object configuredValue = getConfiguredValue(method, modifier);
114+
if (configuredValue != null) {
115+
ReflectionUtils.invokeMethod(method, b, configuredValue);
116+
}
117+
},
115118
(a, b) -> {});
116119
}
117120

@@ -190,10 +193,19 @@ private static Object getConfiguredValue(
190193
return getConfiguredString(configurationMethod, modifier);
191194
} else if (parameterType.isArray()) {
192195
return Array.newInstance(parameterType.getComponentType(), 0);
196+
} else if (parameterType == Map.Entry.class) {
197+
return null;
193198
} else {
194-
throw new IllegalStateException(
195-
String.format("Unable to configure %s", configurationMethod));
199+
for (Class<?> implementation :
200+
org.cloudfoundry.operations.ReflectionUtils.findImplementations(
201+
parameterType)) {
202+
if (isBuiltType(implementation)) {
203+
return getConfiguredBuilder(implementation, modifier);
204+
}
205+
}
196206
}
207+
throw new IllegalStateException(
208+
String.format("Unable to configure %s", configurationMethod));
197209
}
198210

199211
private static List<Method> getMethods(Class<?> builderType) {

0 commit comments

Comments
 (0)