From a883566146cdfc185ab65292fdd0d0c9d71e2777 Mon Sep 17 00:00:00 2001 From: Anil Dhurjaty Date: Tue, 11 Mar 2025 08:30:39 -0700 Subject: [PATCH] fix: resolve ProxyBuilderImpl ClassNotFoundException in parallel import Signed-off-by: Anil Dhurjaty --- CHANGELOG.md | 7 ++++ .../config/service/ClientImportService.java | 3 +- .../service/ClientScopeImportService.java | 3 +- .../config/service/GroupImportService.java | 3 +- .../config/service/RoleImportService.java | 3 +- .../config/service/UserImportService.java | 3 +- .../keycloak/config/util/ParallelUtil.java | 38 +++++++++++++++++++ 7 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 src/main/java/de/adorsys/keycloak/config/util/ParallelUtil.java diff --git a/CHANGELOG.md b/CHANGELOG.md index dd303f8a8..e3a014f28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +## [6.4.1] - 2025-03-11 +### Added +- Explicitly set the class loader in parallel forEach consumers + +### Fixed +- Fix `ClassNotFoundException: org.jboss.resteasy.client.jaxrs.internal.proxy.ProxyBuilderImpl` exception when using parallel imports [#1107](https://github.com/adorsys/keycloak-config-cli/issues/1107) + ## [6.4.0] - 2025-02-21 ### Added - Allow a user's username to be updated through the config [#810](https://github.com/adorsys/keycloak-config-cli/issues/810) diff --git a/src/main/java/de/adorsys/keycloak/config/service/ClientImportService.java b/src/main/java/de/adorsys/keycloak/config/service/ClientImportService.java index ae59dfcdd..0d9cdba18 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/ClientImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/ClientImportService.java @@ -30,6 +30,7 @@ import de.adorsys.keycloak.config.util.ClientScopeUtil; import de.adorsys.keycloak.config.util.CloneUtil; import de.adorsys.keycloak.config.util.KeycloakUtil; +import de.adorsys.keycloak.config.util.ParallelUtil; import de.adorsys.keycloak.config.util.ProtocolMapperUtil; import de.adorsys.keycloak.config.util.ResponseUtil; import org.apache.commons.lang3.ArrayUtils; @@ -111,7 +112,7 @@ private void createOrUpdateClients( ) { Consumer loop = client -> createOrUpdateClient(realmImport, client); if (importConfigProperties.isParallel()) { - clients.parallelStream().forEach(loop); + ParallelUtil.forEach(clients, loop); } else { clients.forEach(loop); } diff --git a/src/main/java/de/adorsys/keycloak/config/service/ClientScopeImportService.java b/src/main/java/de/adorsys/keycloak/config/service/ClientScopeImportService.java index b93ea5b6d..592f88939 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/ClientScopeImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/ClientScopeImportService.java @@ -26,6 +26,7 @@ import de.adorsys.keycloak.config.repository.ClientScopeRepository; import de.adorsys.keycloak.config.repository.RealmRepository; import de.adorsys.keycloak.config.util.CloneUtil; +import de.adorsys.keycloak.config.util.ParallelUtil; import de.adorsys.keycloak.config.util.ProtocolMapperUtil; import org.keycloak.representations.idm.ClientScopeRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; @@ -172,7 +173,7 @@ private void createOrUpdateClientScopes( ) { Consumer loop = clientScope -> createOrUpdateClientScope(realmName, clientScope); if (importConfigProperties.isParallel()) { - clientScopes.parallelStream().forEach(loop); + ParallelUtil.forEach(clientScopes, loop); } else { clientScopes.forEach(loop); } diff --git a/src/main/java/de/adorsys/keycloak/config/service/GroupImportService.java b/src/main/java/de/adorsys/keycloak/config/service/GroupImportService.java index 013a160fe..aa7502fea 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/GroupImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/GroupImportService.java @@ -27,6 +27,7 @@ import de.adorsys.keycloak.config.properties.ImportConfigProperties.ImportManagedProperties.ImportManagedPropertiesValues; import de.adorsys.keycloak.config.repository.GroupRepository; import de.adorsys.keycloak.config.util.CloneUtil; +import de.adorsys.keycloak.config.util.ParallelUtil; import org.keycloak.representations.idm.GroupRepresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,7 +79,7 @@ public void importGroups(RealmImport realmImport) { public void createOrUpdateGroups(List groups, String realmName) { Consumer loop = group -> createOrUpdateRealmGroup(realmName, group); if (importConfigProperties.isParallel()) { - groups.parallelStream().forEach(loop); + ParallelUtil.forEach(groups, loop); } else { groups.forEach(loop); } diff --git a/src/main/java/de/adorsys/keycloak/config/service/RoleImportService.java b/src/main/java/de/adorsys/keycloak/config/service/RoleImportService.java index 447e6815e..f52ea3d9f 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/RoleImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/RoleImportService.java @@ -29,6 +29,7 @@ import de.adorsys.keycloak.config.service.state.StateService; import de.adorsys.keycloak.config.util.CloneUtil; import de.adorsys.keycloak.config.util.KeycloakUtil; +import de.adorsys.keycloak.config.util.ParallelUtil; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.RolesRepresentation; import org.slf4j.Logger; @@ -124,7 +125,7 @@ private void createOrUpdateRealmRoles( ) { Consumer loop = role -> createOrUpdateRealmRole(realmName, role, existingRealmRoles); if (importConfigProperties.isParallel()) { - rolesToImport.parallelStream().forEach(loop); + ParallelUtil.forEach(rolesToImport, loop); } else { rolesToImport.forEach(loop); } diff --git a/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java b/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java index 475c7c35d..1b269b13a 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/UserImportService.java @@ -30,6 +30,7 @@ import de.adorsys.keycloak.config.repository.UserRepository; import de.adorsys.keycloak.config.util.CloneUtil; import de.adorsys.keycloak.config.util.KeycloakUtil; +import de.adorsys.keycloak.config.util.ParallelUtil; import org.keycloak.representations.idm.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -87,7 +88,7 @@ public void doImport(RealmImport realmImport) { Consumer loop = user -> importUser(realmImport.getRealm(), user); if (importConfigProperties.isParallel()) { - users.parallelStream().forEach(loop); + ParallelUtil.forEach(users, loop); } else { users.forEach(loop); } diff --git a/src/main/java/de/adorsys/keycloak/config/util/ParallelUtil.java b/src/main/java/de/adorsys/keycloak/config/util/ParallelUtil.java new file mode 100644 index 000000000..7886b3d24 --- /dev/null +++ b/src/main/java/de/adorsys/keycloak/config/util/ParallelUtil.java @@ -0,0 +1,38 @@ +/*- + * ---license-start + * keycloak-config-cli + * --- + * Copyright (C) 2017 - 2021 adorsys GmbH & Co. KG @ https://adorsys.com + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package de.adorsys.keycloak.config.util; + +import java.util.List; +import java.util.function.Consumer; + +// Override the default implementation of the forEach method in the ParallelUtil class +// Found that the class loader in threads is different from the application class loader, +// leading to [this issue](https://github.com/adorsys/keycloak-config-cli/issues/1107) +public class ParallelUtil { + public static void forEach(List list, Consumer consumer) { + ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); + list.parallelStream() + .forEach(x -> { + Thread.currentThread().setContextClassLoader(originalClassLoader); + consumer.accept(x); + }); + } +}