Skip to content

Commit a6ffa83

Browse files
committed
feat: implement support for converter overriding, add tests
1 parent 36ff178 commit a6ffa83

File tree

4 files changed

+336
-24
lines changed

4 files changed

+336
-24
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,6 @@ public static String contextFor(ControllerConfiguration<?> controllerConfigurati
237237
}
238238
context += "reconciler: " + controllerConfiguration.getName();
239239

240-
241240
return context;
242241
}
243242
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolver.java

Lines changed: 127 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.javaoperatorsdk.operator.api.config.dependent;
22

3+
import java.lang.annotation.Annotation;
34
import java.util.HashMap;
45
import java.util.Map;
56

@@ -14,7 +15,10 @@ public class DependentResourceConfigurationResolver {
1415

1516
private DependentResourceConfigurationResolver() {}
1617

17-
private static final Map<Class, ConfigurationConverter> converters = new HashMap<>();
18+
private static final Map<Class<? extends DependentResource>, ConverterAnnotationPair> converters =
19+
new HashMap<>();
20+
private static final Map<Class<? extends ConfigurationConverter>, ConfigurationConverter> knownConverters =
21+
new HashMap<>();
1822

1923
public static <C extends ControllerConfiguration<? extends HasMetadata>> void configure(
2024
DependentResource dependentResource, DependentResourceSpec spec, C parentConfiguration) {
@@ -38,39 +42,140 @@ public static <C extends ControllerConfiguration<? extends HasMetadata>> Object
3842
}
3943

4044
// find Configured-annotated class if it exists
41-
final var dependentResourceClass = spec.getDependentResourceClass();
42-
Class<?> currentClass = dependentResourceClass;
43-
Configured configured = null;
44-
while (!Object.class.equals(currentClass)) {
45+
return extractConfigurationFromConfigured(spec.getDependentResourceClass(),
46+
parentConfiguration);
47+
}
48+
49+
public static <C extends ControllerConfiguration<? extends HasMetadata>> Object extractConfigurationFromConfigured(
50+
Class<? extends DependentResource> dependentResourceClass, C parentConfiguration) {
51+
var converterAnnotationPair = converters.get(dependentResourceClass);
52+
53+
Annotation configAnnotation;
54+
if (converterAnnotationPair == null) {
55+
var configuredClassPair = getConfigured(dependentResourceClass);
56+
if (configuredClassPair == null) {
57+
return null;
58+
}
59+
60+
// check if we already have a converter registered for the found Configured annotated class
61+
converterAnnotationPair = converters.get(configuredClassPair.annotatedClass);
62+
if (converterAnnotationPair == null) {
63+
final var configured = configuredClassPair.configured;
64+
converterAnnotationPair =
65+
getOrCreateConverter(dependentResourceClass, parentConfiguration,
66+
configured.converter(),
67+
configured.by());
68+
} else {
69+
// only register the converter pair for this dependent resource class as well
70+
converters.put(dependentResourceClass, converterAnnotationPair);
71+
}
72+
}
73+
74+
// find the associated configuration annotation
75+
configAnnotation =
76+
dependentResourceClass.getAnnotation(converterAnnotationPair.annotationClass);
77+
final var converter = converterAnnotationPair.converter;
78+
79+
// always called even if the annotation is null so that implementations can provide default
80+
// values
81+
return converter.configFrom(configAnnotation, parentConfiguration, dependentResourceClass);
82+
}
83+
84+
private static ConfiguredClassPair getConfigured(
85+
Class<? extends DependentResource> dependentResourceClass) {
86+
Class<? extends DependentResource> currentClass = dependentResourceClass;
87+
Configured configured;
88+
ConfiguredClassPair result = null;
89+
while (DependentResource.class.isAssignableFrom(currentClass)) {
4590
configured = currentClass.getAnnotation(Configured.class);
4691
if (configured != null) {
92+
result = new ConfiguredClassPair(configured, currentClass);
4793
break;
4894
}
49-
currentClass = currentClass.getSuperclass();
95+
currentClass = (Class<? extends DependentResource>) currentClass.getSuperclass();
5096
}
97+
return result;
98+
}
5199

100+
private static <C extends ControllerConfiguration<? extends HasMetadata>> ConverterAnnotationPair getOrCreateConverter(
101+
Class<? extends DependentResource> dependentResourceClass, C parentConfiguration,
102+
Class<? extends ConfigurationConverter> converterClass,
103+
Class<? extends Annotation> annotationClass) {
104+
var converterPair = converters.get(dependentResourceClass);
105+
if (converterPair == null) {
106+
// only instantiate a new converter if we haven't done so already for this converter type
107+
var converter = knownConverters.get(converterClass);
108+
if (converter == null) {
109+
converter = Utils.instantiate(converterClass,
110+
ConfigurationConverter.class,
111+
Utils.contextFor(parentConfiguration, dependentResourceClass, Configured.class));
112+
knownConverters.put(converterClass, converter);
113+
}
114+
// record dependent class - converter association for faster future retrieval
115+
converterPair = new ConverterAnnotationPair(converter, annotationClass);
116+
converters.put(dependentResourceClass, converterPair);
117+
}
118+
return converterPair;
119+
}
120+
121+
static ConfigurationConverter getConverter(
122+
Class<? extends DependentResource> dependentResourceClass) {
123+
final var converterAnnotationPair = converters.get(dependentResourceClass);
124+
return converterAnnotationPair != null ? converterAnnotationPair.converter : null;
125+
}
126+
127+
@SuppressWarnings("unused")
128+
public static void registerConverter(Class<? extends DependentResource> dependentResourceClass,
129+
ConfigurationConverter converter) {
130+
var configured = getConfigured(dependentResourceClass);
52131
if (configured == null) {
53-
return null;
132+
throw new IllegalArgumentException("There is no @" + Configured.class.getSimpleName()
133+
+ " annotation on " + dependentResourceClass.getName()
134+
+ " or its superclasses and thus doesn't need to be associated with a converter");
54135
}
136+
55137
// find the associated configuration annotation
56-
final var configAnnotation = dependentResourceClass.getAnnotation(configured.by());
57-
final var converter = getConverter(dependentResourceClass, parentConfiguration, configured);
58-
// always called even if the annotation is null so that implementations can provide default
59-
// values
60-
return converter.configFrom(configAnnotation, parentConfiguration, dependentResourceClass);
138+
final var toRegister = new ConverterAnnotationPair(converter, configured.configured.by());
139+
final Class<? extends ConfigurationConverter> converterClass = converter.getClass();
140+
converters.put(dependentResourceClass, toRegister);
141+
142+
// also register the Configured-annotated class if not the one we're registering
143+
if (!dependentResourceClass.equals(configured.annotatedClass)) {
144+
converters.put(configured.annotatedClass, toRegister);
145+
}
146+
147+
knownConverters.put(converterClass, converter);
61148
}
62149

63-
static <C extends ControllerConfiguration<? extends HasMetadata>> ConfigurationConverter getConverter(
64-
Class<? extends DependentResource> dependentResourceClass, C parentConfiguration,
65-
Configured configured) {
66-
var converter = converters.get(dependentResourceClass);
67-
if (converter == null) {
68-
converter = Utils.instantiate(configured.converter(),
69-
ConfigurationConverter.class,
70-
Utils.contextFor(parentConfiguration, dependentResourceClass, Configured.class));
71-
converters.put(dependentResourceClass, converter);
150+
private static class ConfiguredClassPair {
151+
private final Configured configured;
152+
private final Class<? extends DependentResource> annotatedClass;
153+
154+
private ConfiguredClassPair(Configured configured,
155+
Class<? extends DependentResource> annotatedClass) {
156+
this.configured = configured;
157+
this.annotatedClass = annotatedClass;
158+
}
159+
160+
@Override
161+
public String toString() {
162+
return annotatedClass.getName() + " -> " + configured;
72163
}
73-
return converter;
74164
}
75165

166+
private static class ConverterAnnotationPair {
167+
private final ConfigurationConverter converter;
168+
private final Class<? extends Annotation> annotationClass;
169+
170+
private ConverterAnnotationPair(ConfigurationConverter converter,
171+
Class<? extends Annotation> annotationClass) {
172+
this.converter = converter;
173+
this.annotationClass = annotationClass;
174+
}
175+
176+
@Override
177+
public String toString() {
178+
return converter.toString() + " -> " + annotationClass.getName();
179+
}
180+
}
76181
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentConverter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
public class KubernetesDependentConverter<R extends HasMetadata, P extends HasMetadata> implements
1818
ConfigurationConverter<KubernetesDependent, KubernetesDependentResourceConfig<R>, KubernetesDependentResource<R, P>> {
1919

20-
2120
@Override
2221
@SuppressWarnings({"unchecked", "rawtypes"})
2322
public KubernetesDependentResourceConfig<R> configFrom(KubernetesDependent configAnnotation,

0 commit comments

Comments
 (0)