Managed resource not injected if a dependency is incompatibl (#38196)

Closes #38193

Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
Stian Thorgersen 2025-03-20 08:31:03 +01:00 committed by GitHub
parent c9b88c6bf6
commit 80e5c37969
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 75 additions and 34 deletions

View File

@ -1,6 +1,5 @@
package org.keycloak.testframework.injection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -9,7 +8,6 @@ public abstract class AbstractInterceptorHelper<I, V> {
private final Registry registry;
private final Class<?> interceptorClass;
private final List<Interception> interceptions = new LinkedList<>();
private final InterceptedBy interceptedBy = new InterceptedBy();
public AbstractInterceptorHelper(Registry registry, Class<I> interceptorClass) {
this.registry = registry;
@ -17,21 +15,18 @@ public abstract class AbstractInterceptorHelper<I, V> {
registry.getDeployedInstances().stream().filter(i -> isInterceptor(i.getSupplier())).forEach(i -> interceptions.add(new Interception(i)));
registry.getRequestedInstances().stream().filter(r -> isInterceptor(r.getSupplier())).forEach(r -> interceptions.add(new Interception(r)));
interceptions.forEach(i -> interceptedBy.put(i.supplier, i.instanceId));
}
public boolean sameInterceptors(InstanceContext<?, ?> instanceContext) {
InterceptedBy previousInterceptedBy = instanceContext.getNote("InterceptedBy", InterceptedBy.class);
return interceptedBy.equals(previousInterceptedBy);
}
public V intercept(V value, InstanceContext<?, ?> instanceContext) {
for (Interception interception : interceptions) {
value = intercept(value, interception.supplier, interception.existingInstance);
registry.getLogger().logIntercepted(value, interception.supplier);
if (interception.existingInstance != null) {
interception.existingInstance.registerDependency(instanceContext);
} else {
interception.requestedInstance.registerDependency(instanceContext);
}
}
instanceContext.addNote("InterceptedBy", interceptedBy);
return value;
}
@ -41,24 +36,21 @@ public abstract class AbstractInterceptorHelper<I, V> {
return interceptorClass.isAssignableFrom(supplier.getClass());
}
public static class InterceptedBy extends HashMap<Supplier<?, ?>, Integer> {
}
private static class Interception {
private final int instanceId;
private final Supplier<?, ?> supplier;
private final RequestedInstance<?, ?> requestedInstance;
private final InstanceContext<?, ?> existingInstance;
public Interception(InstanceContext<?, ?> existingInstance) {
this.supplier = existingInstance.getSupplier();
this.instanceId = existingInstance.getInstanceId();
this.requestedInstance = null;
this.existingInstance = existingInstance;
}
public Interception(RequestedInstance<?, ?> requestedInstance) {
this.supplier = requestedInstance.getSupplier();
this.instanceId = requestedInstance.getInstanceId();
this.requestedInstance = requestedInstance;
this.existingInstance = null;
}
}

View File

@ -12,7 +12,7 @@ public class InstanceContext<T, A extends Annotation> {
private final Registry registry;
private final Supplier<T, A> supplier;
private final A annotation;
private final Set<InstanceContext<T, A>> dependencies = new HashSet<>();
private final Set<InstanceContext<?, ?>> dependencies = new HashSet<>();
private T value;
private Class<? extends T> requestedValueType;
private LifeCycle lifeCycle;
@ -73,11 +73,11 @@ public class InstanceContext<T, A extends Annotation> {
return annotation;
}
public Set<InstanceContext<T, A>> getDependencies() {
public Set<InstanceContext<?, ?>> getDependencies() {
return dependencies;
}
public void registerDependency(InstanceContext<T, A> instanceContext) {
public void registerDependency(InstanceContext<?, ?> instanceContext) {
dependencies.add(instanceContext);
}

View File

@ -114,6 +114,7 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
public void beforeEach(Object testInstance) {
findRequestedInstances(testInstance);
destroyIncompatibleInstances();
matchDeployedInstancesWithRequestedInstances();
deployRequestedInstances();
injectFields(testInstance);
@ -145,6 +146,19 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
logger.logRequestedInstances(requestedInstances);
}
private void destroyIncompatibleInstances() {
for (RequestedInstance<?, ?> requestedInstance : requestedInstances) {
InstanceContext deployedInstance = getDeployedInstance(requestedInstance);
if (deployedInstance != null) {
boolean compatible = requestedInstance.getLifeCycle().equals(deployedInstance.getLifeCycle()) && deployedInstance.getSupplier().compatible(deployedInstance, requestedInstance);
if (!compatible) {
logger.logDestroyIncompatible(deployedInstance);
destroy(deployedInstance);
}
}
}
}
private void matchDeployedInstancesWithRequestedInstances() {
Iterator<RequestedInstance<?, ?>> itr = requestedInstances.iterator();
while (itr.hasNext()) {
@ -154,9 +168,6 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
if (requestedInstance.getLifeCycle().equals(deployedInstance.getLifeCycle()) && deployedInstance.getSupplier().compatible(deployedInstance, requestedInstance)) {
logger.logReusingCompatibleInstance(deployedInstance);
itr.remove();
} else {
logger.logDestroyIncompatible(deployedInstance);
destroy(deployedInstance);
}
}
}
@ -172,6 +183,11 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
instance.setValue(requestedInstance.getSupplier().getValue(instance));
deployedInstances.add(instance);
if (!requestedInstance.getDependencies().isEmpty()) {
Set<InstanceContext<?,?>> dependencies = requestedInstance.getDependencies();
dependencies.forEach(instance::registerDependency);
}
logger.logCreatedInstance(requestedInstance, instance);
}
}

View File

@ -1,12 +1,15 @@
package org.keycloak.testframework.injection;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;
public class RequestedInstance<T, A extends Annotation> {
private final int instanceId;
private final Supplier<T, A> supplier;
private final A annotation;
private final Set<InstanceContext<?, ?>> dependencies = new HashSet<>();
private final Class<? extends T> valueType;
private final LifeCycle lifeCycle;
private final String ref;
@ -44,4 +47,11 @@ public class RequestedInstance<T, A extends Annotation> {
return ref;
}
public void registerDependency(InstanceContext<?, ?> instanceContext) {
dependencies.add(instanceContext);
}
public Set<InstanceContext<?, ?>> getDependencies() {
return dependencies;
}
}

View File

@ -58,12 +58,7 @@ public class RealmSupplier implements Supplier<ManagedRealm, InjectRealm> {
@Override
public boolean compatible(InstanceContext<ManagedRealm, InjectRealm> a, RequestedInstance<ManagedRealm, InjectRealm> b) {
if (!a.getAnnotation().config().equals(b.getAnnotation().config())) {
return false;
}
RealmConfigInterceptorHelper interceptor = new RealmConfigInterceptorHelper(a.getRegistry());
return interceptor.sameInterceptors(a);
return a.getAnnotation().config().equals(b.getAnnotation().config());
}
@Override

View File

@ -59,12 +59,7 @@ public abstract class AbstractKeycloakServerSupplier implements Supplier<Keycloa
@Override
public boolean compatible(InstanceContext<KeycloakServer, KeycloakIntegrationTest> a, RequestedInstance<KeycloakServer, KeycloakIntegrationTest> b) {
if (!a.getAnnotation().config().equals(b.getAnnotation().config())) {
return false;
}
ServerConfigInterceptorHelper interceptor = new ServerConfigInterceptorHelper(a.getRegistry());
return interceptor.sameInterceptors(a);
return a.getAnnotation().config().equals(b.getAnnotation().config());
}
@Override

View File

@ -274,6 +274,28 @@ public class RegistryTest {
}
}
@Test
public void testIncompatibleParent() {
MockParentSupplier.COMPATIBLE = false;
RealmIncompatibleParentTest test = new RealmIncompatibleParentTest();
registry.beforeEach(test);
MockParentValue parent1 = test.parent;
MockChildValue child1 = test.child;
Assertions.assertNotNull(test.parent);
Assertions.assertNotNull(test.child);
registry.afterEach();
registry.beforeEach(test);
Assertions.assertNotNull(test.parent);
Assertions.assertNotEquals(parent1, test.parent);
Assertions.assertNotNull(test.child);
Assertions.assertNotEquals(child1, test.child);
}
public static void assertRunning(Object... values) {
MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasItems(values));
MatcherAssert.assertThat(MockInstances.INSTANCES, Matchers.hasSize(values.length));
@ -334,4 +356,15 @@ public class RegistryTest {
@MockChildAnnotation(ref = "ABC", parentRef = "ABC")
MockChildValue childABC;
}
public static final class RealmIncompatibleParentTest {
@MockChildAnnotation
MockChildValue child;
@MockParentAnnotation
MockParentValue parent;
}
}