Clear classes loaded on the server side for run-on-server when a new execution happens (#44909)

Closes #44908

Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
Stian Thorgersen
2025-12-16 10:25:39 +01:00
committed by GitHub
parent 6b300833e2
commit 6bcbd5ab59
4 changed files with 28 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST; import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@@ -13,11 +14,11 @@ import org.keycloak.util.JsonSerialization;
public class RunOnServerRealmResourceProvider implements RealmResourceProvider { public class RunOnServerRealmResourceProvider implements RealmResourceProvider {
private final KeycloakSession session; private final KeycloakSession session;
private final ClassLoader classLoader; private final RunOnServerRealmResourceProviderFactory factory;
public RunOnServerRealmResourceProvider(KeycloakSession session, ClassLoader classLoader) { public RunOnServerRealmResourceProvider(KeycloakSession session, RunOnServerRealmResourceProviderFactory factory) {
this.session = session; this.session = session;
this.classLoader = classLoader; this.factory = factory;
} }
@Override @Override
@@ -33,8 +34,9 @@ public class RunOnServerRealmResourceProvider implements RealmResourceProvider {
@Path("/") @Path("/")
@Consumes(MediaType.TEXT_PLAIN + ";charset=utf-8") @Consumes(MediaType.TEXT_PLAIN + ";charset=utf-8")
@Produces(MediaType.TEXT_PLAIN + ";charset=utf-8") @Produces(MediaType.TEXT_PLAIN + ";charset=utf-8")
public String runOnServer(String runOnServer) { public String runOnServer(String runOnServer, @QueryParam("executionId") String executionId) {
try { try {
ClassLoader classLoader = factory.getTestClassLoader(executionId);
Object o = SerializationUtil.decode(runOnServer, classLoader); Object o = SerializationUtil.decode(runOnServer, classLoader);
if (o instanceof FetchOnServer f) { if (o instanceof FetchOnServer f) {
Object result = f.run(session); Object result = f.run(session);

View File

@@ -11,11 +11,12 @@ public class RunOnServerRealmResourceProviderFactory implements RealmResourcePro
private static final String ID = "testing-run-on-server"; private static final String ID = "testing-run-on-server";
private String executionId;
private ClassLoader testClassLoader; private ClassLoader testClassLoader;
@Override @Override
public RealmResourceProvider create(KeycloakSession session) { public RealmResourceProvider create(KeycloakSession session) {
return new RunOnServerRealmResourceProvider(session, testClassLoader); return new RunOnServerRealmResourceProvider(session, this);
} }
@Override @Override
@@ -31,6 +32,18 @@ public class RunOnServerRealmResourceProviderFactory implements RealmResourcePro
return ID; return ID;
} }
public ClassLoader getTestClassLoader(String executionId) {
if (!executionId.equals(this.executionId)) {
try {
testClassLoader = new TestClassLoader();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
this.executionId = executionId;
}
return testClassLoader;
}
@Override @Override
public void init(org.keycloak.Config.Scope config) { public void init(org.keycloak.Config.Scope config) {
try { try {

View File

@@ -23,10 +23,12 @@ public class RunOnServerClient {
private static final String RUN_ON_SERVER_ENDPOINT = "/testing-run-on-server"; private static final String RUN_ON_SERVER_ENDPOINT = "/testing-run-on-server";
private final HttpClient httpClient; private final HttpClient httpClient;
private final String url; private final String url;
private final int executionId;
public RunOnServerClient(HttpClient httpClient, String realmUrl) { public RunOnServerClient(HttpClient httpClient, String realmUrl, int executionId) {
this.httpClient = httpClient; this.httpClient = httpClient;
this.url = realmUrl + RUN_ON_SERVER_ENDPOINT; this.url = realmUrl + RUN_ON_SERVER_ENDPOINT;
this.executionId = executionId;
} }
public <T> T fetch(FetchOnServerWrapper<T> wrapper) throws RunOnServerException { public <T> T fetch(FetchOnServerWrapper<T> wrapper) throws RunOnServerException {
@@ -74,7 +76,7 @@ public class RunOnServerClient {
public String runOnServer(String encoded) throws RunOnServerException { public String runOnServer(String encoded) throws RunOnServerException {
try { try {
HttpPost request = new HttpPost(url); HttpPost request = new HttpPost(url + "?executionId=" + executionId);
request.setHeader("Content-type", "text/plain;charset=utf-8"); request.setHeader("Content-type", "text/plain;charset=utf-8");
request.setEntity(new StringEntity(encoded)); request.setEntity(new StringEntity(encoded));

View File

@@ -10,6 +10,7 @@ import org.keycloak.testframework.injection.Supplier;
import org.keycloak.testframework.injection.SupplierOrder; import org.keycloak.testframework.injection.SupplierOrder;
import org.keycloak.testframework.realm.ManagedRealm; import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.remote.RemoteProviders; import org.keycloak.testframework.remote.RemoteProviders;
import org.keycloak.testframework.server.KeycloakServer;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
@@ -17,6 +18,8 @@ public class RunOnServerSupplier implements Supplier<RunOnServerClient, InjectRu
@Override @Override
public RunOnServerClient getValue(InstanceContext<RunOnServerClient, InjectRunOnServer> instanceContext) { public RunOnServerClient getValue(InstanceContext<RunOnServerClient, InjectRunOnServer> instanceContext) {
KeycloakServer server = instanceContext.getDependency(KeycloakServer.class);
HttpClient httpClient = instanceContext.getDependency(HttpClient.class); HttpClient httpClient = instanceContext.getDependency(HttpClient.class);
ManagedRealm realm = instanceContext.getDependency(ManagedRealm.class, instanceContext.getAnnotation().realmRef()); ManagedRealm realm = instanceContext.getDependency(ManagedRealm.class, instanceContext.getAnnotation().realmRef());
instanceContext.getDependency(RemoteProviders.class); instanceContext.getDependency(RemoteProviders.class);
@@ -25,7 +28,7 @@ public class RunOnServerSupplier implements Supplier<RunOnServerClient, InjectRu
String[] permittedPackages = instanceContext.getAnnotation().permittedPackages(); String[] permittedPackages = instanceContext.getAnnotation().permittedPackages();
testClassServer.addPermittedPackages(new HashSet<>(Arrays.asList(permittedPackages))); testClassServer.addPermittedPackages(new HashSet<>(Arrays.asList(permittedPackages)));
return new RunOnServerClient(httpClient, realm.getBaseUrl()); return new RunOnServerClient(httpClient, realm.getBaseUrl(), server.hashCode());
} }
@Override @Override