diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java index 23c5cd526d..21ea2628c5 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java @@ -612,18 +612,18 @@ public class ClientAdapter implements ClientModel, CachedObject { } @Override - public Set getRoles() { - return cacheSession.getClientRoles(cachedRealm, this); + public Stream getRolesStream() { + return cacheSession.getClientRolesStream(cachedRealm, this); } @Override - public Set getRoles(Integer first, Integer max) { - return cacheSession.getClientRoles(cachedRealm, this, first, max); + public Stream getRolesStream(Integer first, Integer max) { + return cacheSession.getClientRolesStream(cachedRealm, this, first, max); } @Override - public Set searchForRoles(String search, Integer first, Integer max) { - return cacheSession.searchForClientRoles(cachedRealm, this, search, first, max); + public Stream searchForRolesStream(String search, Integer first, Integer max) { + return cacheSession.searchForClientRolesStream(cachedRealm, this, search, first, max); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java index ba0ff2cd7b..2072d96446 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java @@ -29,6 +29,7 @@ import org.keycloak.storage.client.ClientStorageProvider; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; /** * @author Bill Burke @@ -755,13 +756,13 @@ public class RealmAdapter implements CachedRealmModel { } @Override - public List getClients() { - return cacheSession.getClients(this); + public Stream getClientsStream() { + return cacheSession.getClientsStream(this); } @Override - public List getAlwaysDisplayInConsoleClients() { - return cacheSession.getAlwaysDisplayInConsoleClients(this); + public Stream getAlwaysDisplayInConsoleClientsStream() { + return cacheSession.getAlwaysDisplayInConsoleClientsStream(this); } @Override @@ -791,13 +792,13 @@ public class RealmAdapter implements CachedRealmModel { } @Override - public List searchClientByClientId(String clientId, Integer firstResult, Integer maxResults) { - return cacheSession.searchClientsByClientId(this, clientId, firstResult, maxResults); + public Stream searchClientByClientIdStream(String clientId, Integer firstResult, Integer maxResults) { + return cacheSession.searchClientsByClientIdStream(this, clientId, firstResult, maxResults); } @Override - public List getClients(Integer firstResult, Integer maxResults) { - return cacheSession.getClients(this, firstResult, maxResults); + public Stream getClientsStream(Integer firstResult, Integer maxResults) { + return cacheSession.getClientsStream(this, firstResult, maxResults); } @Override @@ -1022,18 +1023,18 @@ public class RealmAdapter implements CachedRealmModel { } @Override - public Set getRoles() { - return cacheSession.getRealmRoles(this); + public Stream getRolesStream() { + return cacheSession.getRealmRolesStream(this); } @Override - public Set getRoles(Integer first, Integer max) { - return cacheSession.getRealmRoles(this, first, max); + public Stream getRolesStream(Integer first, Integer max) { + return cacheSession.getRealmRolesStream(this, first, max); } @Override - public Set searchForRoles(String search, Integer first, Integer max) { - return cacheSession.searchForRoles(this, search, first, max); + public Stream searchForRolesStream(String search, Integer first, Integer max) { + return cacheSession.searchForRolesStream(this, search, first, max); } @Override diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java index a5e6ddc1d8..61790595d4 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java @@ -31,6 +31,8 @@ import org.keycloak.storage.StorageId; import org.keycloak.storage.client.ClientStorageProviderModel; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** @@ -538,18 +540,18 @@ public class RealmCacheSession implements CacheRealmProvider { } @Override - public List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { - return getClientDelegate().getClients(realm, firstResult, maxResults); + public Stream getClientsStream(RealmModel realm, Integer firstResult, Integer maxResults) { + return getClientDelegate().getClientsStream(realm, firstResult, maxResults); } @Override - public List getClients(RealmModel realm) { - return getClientDelegate().getClients(realm); + public Stream getClientsStream(RealmModel realm) { + return getClientDelegate().getClientsStream(realm); } @Override - public List getAlwaysDisplayInConsoleClients(RealmModel realm) { - return getClientDelegate().getAlwaysDisplayInConsoleClients(realm); + public Stream getAlwaysDisplayInConsoleClientsStream(RealmModel realm) { + return getClientDelegate().getAlwaysDisplayInConsoleClientsStream(realm); } @Override @@ -604,11 +606,11 @@ public class RealmCacheSession implements CacheRealmProvider { } @Override - public Set getRealmRoles(RealmModel realm) { + public Stream getRealmRolesStream(RealmModel realm) { String cacheKey = getRolesCacheKey(realm.getId()); boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId()); if (queryDB) { - return getRealmDelegate().getRealmRoles(realm); + return getRealmDelegate().getRealmRolesStream(realm); } RoleListQuery query = cache.get(cacheKey, RoleListQuery.class); @@ -618,33 +620,33 @@ public class RealmCacheSession implements CacheRealmProvider { if (query == null) { Long loaded = cache.getCurrentRevision(cacheKey); + // intentionally using deprecated method here because role ids needs to be collected for cashing while the stream returned Set model = getRealmDelegate().getRealmRoles(realm); if (model == null) return null; - Set ids = new HashSet<>(); - for (RoleModel role : model) ids.add(role.getId()); + Set ids = model.stream().map(RoleModel::getId).collect(Collectors.toSet()); query = new RoleListQuery(loaded, cacheKey, realm, ids); logger.tracev("adding realm roles cache miss: realm {0} key {1}", realm.getName(), cacheKey); cache.addRevisioned(query, startupRevision); - return model; + return model.stream(); } Set list = new HashSet<>(); for (String id : query.getRoles()) { RoleModel role = session.realms().getRoleById(id, realm); if (role == null) { invalidations.add(cacheKey); - return getRealmDelegate().getRealmRoles(realm); + return getRealmDelegate().getRealmRolesStream(realm); } list.add(role); } - return list; + return list.stream(); } @Override - public Set getClientRoles(RealmModel realm, ClientModel client) { + public Stream getClientRolesStream(RealmModel realm, ClientModel client) { String cacheKey = getRolesCacheKey(client.getId()); boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId()) || listInvalidations.contains(realm.getId()); if (queryDB) { - return getRealmDelegate().getClientRoles(realm, client); + return getRealmDelegate().getClientRolesStream(realm, client, null, null); } RoleListQuery query = cache.get(cacheKey, RoleListQuery.class); @@ -654,46 +656,46 @@ public class RealmCacheSession implements CacheRealmProvider { if (query == null) { Long loaded = cache.getCurrentRevision(cacheKey); - Set model = getRealmDelegate().getClientRoles(realm, client); + // intentionally using deprecated method here because role ids needs to be collected for cashing while the stream returned + Set model = getRealmDelegate().getClientRoles(realm, client, null, null); if (model == null) return null; - Set ids = new HashSet<>(); - for (RoleModel role : model) ids.add(role.getId()); + Set ids = model.stream().map(RoleModel::getId).collect(Collectors.toSet()); query = new RoleListQuery(loaded, cacheKey, realm, ids, client.getClientId()); logger.tracev("adding client roles cache miss: client {0} key {1}", client.getClientId(), cacheKey); cache.addRevisioned(query, startupRevision); - return model; + return model.stream(); } Set list = new HashSet<>(); for (String id : query.getRoles()) { RoleModel role = session.realms().getRoleById(id, realm); if (role == null) { invalidations.add(cacheKey); - return getRealmDelegate().getClientRoles(realm, client); + return getRealmDelegate().getClientRolesStream(realm, client, null, null); } list.add(role); } - return list; + return list.stream(); } @Override - public Set getRealmRoles(RealmModel realm, Integer first, Integer max) { - return getRealmDelegate().getRealmRoles(realm, first, max); + public Stream getRealmRolesStream(RealmModel realm, Integer first, Integer max) { + return getRealmDelegate().getRealmRolesStream(realm, first, max); } @Override - public Set getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max) { - return getRealmDelegate().getClientRoles(realm, client, first, max); + public Stream getClientRolesStream(RealmModel realm, ClientModel client, Integer first, Integer max) { + return getRealmDelegate().getClientRolesStream(realm, client, first, max); } @Override - public Set searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first, + public Stream searchForClientRolesStream(RealmModel realm, ClientModel client, String search, Integer first, Integer max) { - return getRealmDelegate().searchForClientRoles(realm, client, search, first, max); + return getRealmDelegate().searchForClientRolesStream(realm, client, search, first, max); } @Override - public Set searchForRoles(RealmModel realm, String search, Integer first, Integer max) { - return getRealmDelegate().searchForRoles(realm, search, first, max); + public Stream searchForRolesStream(RealmModel realm, String search, Integer first, Integer max) { + return getRealmDelegate().searchForRolesStream(realm, search, first, max); } @Override @@ -1128,8 +1130,8 @@ public class RealmCacheSession implements CacheRealmProvider { } @Override - public List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { - return getClientDelegate().searchClientsByClientId(realm, clientId, firstResult, maxResults); + public Stream searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { + return getClientDelegate().searchClientsByClientIdStream(realm, clientId, firstResult, maxResults); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java index db01efaf23..0f965babb2 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java @@ -671,18 +671,18 @@ public class ClientAdapter implements ClientModel, JpaModel { } @Override - public Set getRoles() { - return session.realms().getClientRoles(realm, this); + public Stream getRolesStream() { + return session.realms().getClientRolesStream(realm, this, null, null); } @Override - public Set getRoles(Integer first, Integer max) { - return session.realms().getClientRoles(realm, this, first, max); + public Stream getRolesStream(Integer first, Integer max) { + return session.realms().getClientRolesStream(realm, this, first, max); } @Override - public Set searchForRoles(String search, Integer first, Integer max) { - return session.realms().searchForClientRoles(realm, this, search, first, max); + public Stream searchForRolesStream(String search, Integer first, Integer max) { + return session.realms().searchForClientRolesStream(realm, this, search, first, max); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java index d1cd2c44bc..57af7642ca 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java @@ -46,6 +46,8 @@ import javax.persistence.TypedQuery; import java.util.*; import java.util.stream.Collectors; +import java.util.stream.Stream; + import org.keycloak.models.ModelException; import static org.keycloak.common.util.StackUtil.getShortStackTrace; @@ -253,17 +255,12 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { } @Override - public Set getRealmRoles(RealmModel realm) { + public Stream getRealmRolesStream(RealmModel realm) { TypedQuery query = em.createNamedQuery("getRealmRoleIds", String.class); query.setParameter("realm", realm.getId()); - List roles = query.getResultList(); + Stream roles = query.getResultStream(); - if (roles.isEmpty()) return Collections.EMPTY_SET; - Set list = new HashSet<>(); - for (String id : roles) { - list.add(session.realms().getRoleById(id, realm)); - } - return Collections.unmodifiableSet(list); + return roles.map(realm::getRoleById); } @Override @@ -275,66 +272,50 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { if (roles.isEmpty()) return null; return session.realms().getRoleById(roles.get(0), realm); } - - - @Override - public Set getClientRoles(RealmModel realm, ClientModel client) { - Set list = new HashSet<>(); - TypedQuery query = em.createNamedQuery("getClientRoleIds", String.class); - query.setParameter("client", client.getId()); - List roles = query.getResultList(); - for (String id : roles) { - list.add(session.realms().getRoleById(id, realm)); - } - return list; - } @Override - public Set getRealmRoles(RealmModel realm, Integer first, Integer max) { + public Stream getRealmRolesStream(RealmModel realm, Integer first, Integer max) { TypedQuery query = em.createNamedQuery("getRealmRoles", RoleEntity.class); query.setParameter("realm", realm.getId()); - return getRoles(query, realm, first, max); + return getRolesStream(query, realm, first, max); } @Override - public Set getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max) { + public Stream getClientRolesStream(RealmModel realm, ClientModel client, Integer first, Integer max) { TypedQuery query = em.createNamedQuery("getClientRoles", RoleEntity.class); query.setParameter("client", client.getId()); - return getRoles(query, realm, first, max); + return getRolesStream(query, realm, first, max); } - protected Set getRoles(TypedQuery query, RealmModel realm, Integer first, Integer max) { + protected Stream getRolesStream(TypedQuery query, RealmModel realm, Integer first, Integer max) { if(Objects.nonNull(first) && Objects.nonNull(max) && first >= 0 && max >= 0) { query= query.setFirstResult(first).setMaxResults(max); } - List results = query.getResultList(); - - return results.stream() - .map(role -> new RoleAdapter(session, realm, em, role)) - .collect(Collectors.collectingAndThen( - Collectors.toCollection(LinkedHashSet::new), Collections::unmodifiableSet)); + Stream results = query.getResultStream(); + + return results.map(role -> new RoleAdapter(session, realm, em, role)); } @Override - public Set searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first, Integer max) { + public Stream searchForClientRolesStream(RealmModel realm, ClientModel client, String search, Integer first, Integer max) { TypedQuery query = em.createNamedQuery("searchForClientRoles", RoleEntity.class); query.setParameter("client", client.getId()); return searchForRoles(query, realm, search, first, max); } @Override - public Set searchForRoles(RealmModel realm, String search, Integer first, Integer max) { + public Stream searchForRolesStream(RealmModel realm, String search, Integer first, Integer max) { TypedQuery query = em.createNamedQuery("searchForRealmRoles", RoleEntity.class); query.setParameter("realm", realm.getId()); return searchForRoles(query, realm, search, first, max); } - protected Set searchForRoles(TypedQuery query, RealmModel realm, String search, Integer first, Integer max) { + protected Stream searchForRoles(TypedQuery query, RealmModel realm, String search, Integer first, Integer max) { query.setParameter("search", "%" + search.trim().toLowerCase() + "%"); if(Objects.nonNull(first) && Objects.nonNull(max) @@ -342,12 +323,9 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { query= query.setFirstResult(first).setMaxResults(max); } - List results = query.getResultList(); + Stream results = query.getResultStream(); - return results.stream() - .map(role -> new RoleAdapter(session, realm, em, role)) - .collect(Collectors.collectingAndThen( - Collectors.toSet(), Collections::unmodifiableSet)); + return results.map(role -> new RoleAdapter(session, realm, em, role)); } @Override @@ -364,7 +342,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { } String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em); em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate(); - realm.getClients().forEach(c -> c.deleteScopeMapping(role)); + realm.getClientsStream().forEach(c -> c.deleteScopeMapping(role)); em.createNamedQuery("deleteClientScopeRoleMappingByRole").setParameter("role", roleEntity).executeUpdate(); int val = em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate(); @@ -614,7 +592,12 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { } @Override - public List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { + public Stream getClientsStream(RealmModel realm) { + return getClientsStream(realm, null, null); + } + + @Override + public Stream getClientsStream(RealmModel realm, Integer firstResult, Integer maxResults) { TypedQuery query = em.createNamedQuery("getClientIdsByRealm", String.class); if (firstResult != null && firstResult > 0) { query.setFirstResult(firstResult); @@ -623,28 +606,18 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { query.setMaxResults(maxResults); } query.setParameter("realm", realm.getId()); - List clients = query.getResultList(); - if (clients.isEmpty()) return Collections.EMPTY_LIST; - List list = new LinkedList<>(); - for (String id : clients) { - ClientModel client = session.clients().getClientById(realm, id); - if (client != null) list.add(client); - } - return Collections.unmodifiableList(list); + Stream clients = query.getResultStream(); + + return clients.map(c -> session.clients().getClientById(realm, c)).filter(Objects::nonNull); } @Override - public List getAlwaysDisplayInConsoleClients(RealmModel realm) { + public Stream getAlwaysDisplayInConsoleClientsStream(RealmModel realm) { TypedQuery query = em.createNamedQuery("getAlwaysDisplayInConsoleClients", String.class); query.setParameter("realm", realm.getId()); - List clients = query.getResultList(); - if (clients.isEmpty()) return Collections.EMPTY_LIST; - List list = new LinkedList<>(); - for (String id : clients) { - ClientModel client = session.clients().getClientById(realm, id); - if (client != null) list.add(client); - } - return Collections.unmodifiableList(list); + Stream clientStream = query.getResultStream(); + + return clientStream.map(c -> session.clients().getClientById(realm, c)).filter(Objects::nonNull); } @Override @@ -673,7 +646,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { } @Override - public List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { + public Stream searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { TypedQuery query = em.createNamedQuery("searchClientsByClientId", String.class); if (firstResult != null && firstResult > 0) { query.setFirstResult(firstResult); @@ -683,9 +656,8 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider { } query.setParameter("clientId", clientId); query.setParameter("realm", realm.getId()); - List results = query.getResultList(); - if (results.isEmpty()) return Collections.EMPTY_LIST; - return results.stream().map(id -> session.clients().getClientById(realm, id)).collect(Collectors.toList()); + Stream results = query.getResultStream(); + return results.map(c -> session.clients().getClientById(realm, c)); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java index a140a37968..bab75626fa 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java @@ -34,6 +34,7 @@ import javax.persistence.TypedQuery; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.util.Objects.nonNull; @@ -829,18 +830,18 @@ public class RealmAdapter implements RealmModel, JpaModel { } @Override - public List getClients() { - return session.clients().getClients(this); + public Stream getClientsStream() { + return session.clients().getClientsStream(this); } @Override - public List getClients(Integer firstResult, Integer maxResults) { - return session.clients().getClients(this, firstResult, maxResults); + public Stream getClientsStream(Integer firstResult, Integer maxResults) { + return session.clients().getClientsStream(this, firstResult, maxResults); } @Override - public List getAlwaysDisplayInConsoleClients() { - return session.clients().getAlwaysDisplayInConsoleClients(this); + public Stream getAlwaysDisplayInConsoleClientsStream() { + return session.clients().getAlwaysDisplayInConsoleClientsStream(this); } @Override @@ -872,8 +873,8 @@ public class RealmAdapter implements RealmModel, JpaModel { } @Override - public List searchClientByClientId(String clientId, Integer firstResult, Integer maxResults) { - return session.clients().searchClientsByClientId(this, clientId, firstResult, maxResults); + public Stream searchClientByClientIdStream(String clientId, Integer firstResult, Integer maxResults) { + return session.clients().searchClientsByClientIdStream(this, clientId, firstResult, maxResults); } private static final String BROWSER_HEADER_PREFIX = "_browser_header."; @@ -933,18 +934,18 @@ public class RealmAdapter implements RealmModel, JpaModel { } @Override - public Set getRoles() { - return session.realms().getRealmRoles(this); + public Stream getRolesStream() { + return session.realms().getRealmRolesStream(this); } @Override - public Set getRoles(Integer first, Integer max) { - return session.realms().getRealmRoles(this, first, max); + public Stream getRolesStream(Integer first, Integer max) { + return session.realms().getRealmRolesStream(this, first, max); } @Override - public Set searchForRoles(String search, Integer first, Integer max) { - return session.realms().searchForRoles(this, search, first, max); + public Stream searchForRolesStream(String search, Integer first, Integer max) { + return session.realms().searchForRolesStream(this, search, first, max); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/client/AbstractClientModel.java b/model/map/src/main/java/org/keycloak/models/map/client/AbstractClientModel.java index b5664e2153..89255fcb86 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/AbstractClientModel.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/AbstractClientModel.java @@ -25,6 +25,7 @@ import org.keycloak.models.map.common.AbstractEntity; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @@ -81,18 +82,18 @@ public abstract class AbstractClientModel implements C } @Override - public Set getRoles() { - return session.realms().getClientRoles(realm, this); + public Stream getRolesStream() { + return session.realms().getClientRolesStream(realm, this, null, null); } @Override - public Set getRoles(Integer firstResult, Integer maxResults) { - return session.realms().getClientRoles(realm, this, firstResult, maxResults); + public Stream getRolesStream(Integer firstResult, Integer maxResults) { + return session.realms().getClientRolesStream(realm, this, firstResult, maxResults); } @Override - public Set searchForRoles(String search, Integer first, Integer max) { - return session.realms().searchForClientRoles(realm, this, search, first, max); + public Stream searchForRolesStream(String search, Integer first, Integer max) { + return session.realms().searchForClientRolesStream(realm, this, search, first, max); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java index b3dbef6b67..546ec40f4d 100644 --- a/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/client/MapClientProvider.java @@ -29,9 +29,7 @@ import org.keycloak.models.RealmProvider; import org.keycloak.models.RoleModel; import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.common.Serialization; -import java.util.Collections; import java.util.Comparator; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -134,15 +132,15 @@ public class MapClientProvider implements ClientProvider { } @Override - public List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { + public Stream getClientsStream(RealmModel realm, Integer firstResult, Integer maxResults) { Stream s = getClientsStream(realm); - if (firstResult >= 0) { + if (firstResult != null && firstResult >= 0) { s = s.skip(firstResult); } - if (maxResults >= 0) { + if (maxResults != null && maxResults >= 0) { s = s.limit(maxResults); } - return s.collect(Collectors.toList()); + return s; } private Stream getNotRemovedUpdatedClientsStream() { @@ -152,7 +150,7 @@ public class MapClientProvider implements ClientProvider { return Stream.concat(tx.createdValuesStream(clientStore.keySet()), updatedAndNotRemovedClientsStream); } -// @Override + @Override public Stream getClientsStream(RealmModel realm) { return getNotRemovedUpdatedClientsStream() .filter(entityRealmFilter(realm)) @@ -161,11 +159,6 @@ public class MapClientProvider implements ClientProvider { ; } - @Override - public List getClients(RealmModel realm) { - return getClientsStream(realm).collect(Collectors.toList()); - } - @Override public ClientModel addClient(RealmModel realm, String id, String clientId) { final UUID entityId = id == null ? UUID.randomUUID() : UUID.fromString(id); @@ -194,10 +187,9 @@ public class MapClientProvider implements ClientProvider { } @Override - public List getAlwaysDisplayInConsoleClients(RealmModel realm) { + public Stream getAlwaysDisplayInConsoleClientsStream(RealmModel realm) { return getClientsStream(realm) - .filter(ClientModel::isAlwaysDisplayInConsole) - .collect(Collectors.toList()); + .filter(ClientModel::isAlwaysDisplayInConsole); } @Override @@ -285,9 +277,9 @@ public class MapClientProvider implements ClientProvider { } @Override - public List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { + public Stream searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { if (clientId == null) { - return Collections.EMPTY_LIST; + return Stream.empty(); } String clientIdLower = clientId.toLowerCase(); Stream s = getNotRemovedUpdatedClientsStream() @@ -295,17 +287,14 @@ public class MapClientProvider implements ClientProvider { .filter(entity -> entity.getClientId() != null && entity.getClientId().toLowerCase().contains(clientIdLower)) .sorted(COMPARE_BY_CLIENT_ID); - if (firstResult >= 0) { + if (firstResult != null && firstResult >= 0) { s = s.skip(firstResult); } - if (maxResults >= 0) { + if (maxResults != null && maxResults >= 0) { s = s.limit(maxResults); } - return s - .map(entityToAdapterFunc(realm)) - .collect(Collectors.toList()) - ; + return s.map(entityToAdapterFunc(realm)); } @Override diff --git a/server-spi-private/src/main/java/org/keycloak/storage/client/UnsupportedOperationsClientStorageAdapter.java b/server-spi-private/src/main/java/org/keycloak/storage/client/UnsupportedOperationsClientStorageAdapter.java index 7a19e1cd8c..2f61ed360c 100644 --- a/server-spi-private/src/main/java/org/keycloak/storage/client/UnsupportedOperationsClientStorageAdapter.java +++ b/server-spi-private/src/main/java/org/keycloak/storage/client/UnsupportedOperationsClientStorageAdapter.java @@ -22,7 +22,7 @@ import org.keycloak.models.RoleModel; import java.util.Collections; import java.util.List; -import java.util.Set; +import java.util.stream.Stream; /** * Base helper class. Unsupported operations are implemented here that throw exception on invocation. @@ -52,18 +52,18 @@ public abstract class UnsupportedOperationsClientStorageAdapter implements Clien } @Override - public final Set getRoles() { - return Collections.EMPTY_SET; + public final Stream getRolesStream() { + return Stream.empty(); } @Override - public final Set getRoles(Integer first, Integer max) { - return Collections.EMPTY_SET; + public final Stream getRolesStream(Integer first, Integer max) { + return Stream.empty(); } @Override - public final Set searchForRoles(String search, Integer first, Integer max) { - return Collections.EMPTY_SET; + public final Stream searchForRolesStream(String search, Integer first, Integer max) { + return Stream.empty(); } @Override diff --git a/server-spi/src/main/java/org/keycloak/models/ClientModel.java b/server-spi/src/main/java/org/keycloak/models/ClientModel.java index 0b7b802b77..c470275d09 100755 --- a/server-spi/src/main/java/org/keycloak/models/ClientModel.java +++ b/server-spi/src/main/java/org/keycloak/models/ClientModel.java @@ -17,7 +17,6 @@ package org.keycloak.models; -import java.util.List; import java.util.Map; import java.util.Set; diff --git a/server-spi/src/main/java/org/keycloak/models/ClientProvider.java b/server-spi/src/main/java/org/keycloak/models/ClientProvider.java index 2df6c7197a..2eddea5cc3 100644 --- a/server-spi/src/main/java/org/keycloak/models/ClientProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/ClientProvider.java @@ -20,6 +20,8 @@ import org.keycloak.provider.Provider; import org.keycloak.storage.client.ClientLookupProvider; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Provider of the client records. @@ -31,21 +33,46 @@ public interface ClientProvider extends ClientLookupProvider, Provider { /** * Returns the clients of the given realm. + * @deprecated use the stream variant instead * @param realm Realm. * @param firstResult First result to return. Ignored if negative or {@code null}. - * @param maxResults Maximim number of results to return. Ignored if negative or {@code null}. + * @param maxResults Maximum number of results to return. Ignored if negative or {@code null}. * @return List of the clients. Never returns {@code null}. */ - List getClients(RealmModel realm, Integer firstResult, Integer maxResults); + @Deprecated + default List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { + return getClientsStream(realm, firstResult, maxResults).collect(Collectors.toList()); + } + + /** + * Returns the clients of the given realm as a stream. + * @param realm Realm. + * @param firstResult First result to return. Ignored if negative or {@code null}. + * @param maxResults Maximum number of results to return. Ignored if negative or {@code null}. + * @return Stream of the clients. Never returns {@code null}. + */ + Stream getClientsStream(RealmModel realm, Integer firstResult, Integer maxResults); /** * Returns all the clients of the given realm. + * @deprecated use the stream variant instead * Effectively the same as the call {@code getClients(realm, null, null)}. * @param realm Realm. * @return List of the clients. Never returns {@code null}. */ + @Deprecated default List getClients(RealmModel realm) { - return this.getClients(realm, null, null); + return this.getClientsStream(realm, null, null).collect(Collectors.toList()); + } + + /** + * Returns all the clients of the given realm as a stream. + * Effectively the same as the call {@code getClientsStream(realm, null, null)}. + * @param realm Realm. + * @return Stream of the clients. Never returns {@code null}. + */ + default Stream getClientsStream(RealmModel realm) { + return this.getClientsStream(realm, null, null); } /** @@ -81,10 +108,21 @@ public interface ClientProvider extends ClientLookupProvider, Provider { /** * Returns a list of clients that are expected to always show up in account console. + * @deprecated use the stream variant instead * @param realm Realm owning the clients. * @return List of the clients. Never returns {@code null}. */ - List getAlwaysDisplayInConsoleClients(RealmModel realm); + @Deprecated + default List getAlwaysDisplayInConsoleClients(RealmModel realm) { + return getAlwaysDisplayInConsoleClientsStream(realm).collect(Collectors.toList()); + } + + /** + * Returns a stream of clients that are expected to always show up in account console. + * @param realm Realm owning the clients. + * @return Stream of the clients. Never returns {@code null}. + */ + Stream getAlwaysDisplayInConsoleClientsStream(RealmModel realm); /** * Removes given client from the given realm. diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java index b262c3c6dd..5f8baf2298 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java @@ -26,6 +26,8 @@ import org.keycloak.storage.client.ClientStorageProvider; import org.keycloak.storage.client.ClientStorageProviderModel; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -284,11 +286,28 @@ public interface RealmModel extends RoleContainerModel { void removeDefaultGroup(GroupModel group); - List getClients(); - List getClients(Integer firstResult, Integer maxResults); + @Deprecated + default List getClients() { + return getClientsStream(null, null).collect(Collectors.toList()); + } + + Stream getClientsStream(); + + @Deprecated + default List getClients(Integer firstResult, Integer maxResults) { + return getClientsStream(firstResult, maxResults).collect(Collectors.toList()); + } + + Stream getClientsStream(Integer firstResult, Integer maxResults); + Long getClientsCount(); - List getAlwaysDisplayInConsoleClients(); + @Deprecated + default List getAlwaysDisplayInConsoleClients() { + return getAlwaysDisplayInConsoleClientsStream().collect(Collectors.toList()); + } + + Stream getAlwaysDisplayInConsoleClientsStream(); ClientModel addClient(String name); @@ -298,7 +317,13 @@ public interface RealmModel extends RoleContainerModel { ClientModel getClientById(String id); ClientModel getClientByClientId(String clientId); - List searchClientByClientId(String clientId, Integer firstResult, Integer maxResults); + + @Deprecated + default List searchClientByClientId(String clientId, Integer firstResult, Integer maxResults) { + return searchClientByClientIdStream(clientId, firstResult, maxResults).collect(Collectors.toList()); + } + + Stream searchClientByClientIdStream(String clientId, Integer firstResult, Integer maxResults); void updateRequiredCredentials(Set creds); diff --git a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java index 75334b89cf..54bf6623bf 100755 --- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java +++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java @@ -22,6 +22,8 @@ import org.keycloak.provider.Provider; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -81,16 +83,44 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio RoleModel getRealmRole(RealmModel realm, String name); - Set getRealmRoles(RealmModel realm); - - Set getRealmRoles(RealmModel realm, Integer first, Integer max); - - Set getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max); - - Set searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first, - Integer max); - - Set searchForRoles(RealmModel realm, String search, Integer first, Integer max); + // TODO switch all usages to the stream variant + @Deprecated + default Set getRealmRoles(RealmModel realm) { + return getRealmRolesStream(realm).collect(Collectors.toSet()); + } + + Stream getRealmRolesStream(RealmModel realm); + + @Deprecated + default Set getRealmRoles(RealmModel realm, Integer first, Integer max) { + return getRealmRolesStream(realm, first, max).collect(Collectors.toSet()); + } + + Stream getRealmRolesStream(RealmModel realm, Integer first, Integer max); + + // TODO switch all usages to the stream variant + @Deprecated + default Set getClientRoles(RealmModel realm, ClientModel client, Integer first, Integer max) { + return getClientRolesStream(realm, client, first, max).collect(Collectors.toSet()); + } + + Stream getClientRolesStream(RealmModel realm, ClientModel client, Integer first, Integer max); + + @Deprecated + default Set searchForClientRoles(RealmModel realm, ClientModel client, String search, Integer first, + Integer max) { + return searchForClientRolesStream(realm, client, search, first, max).collect(Collectors.toSet()); + } + + Stream searchForClientRolesStream(RealmModel realm, ClientModel client, String search, Integer first, + Integer max); + + @Deprecated + default Set searchForRoles(RealmModel realm, String search, Integer first, Integer max) { + return searchForRolesStream(realm, search, first, max).collect(Collectors.toSet()); + } + + Stream searchForRolesStream(RealmModel realm, String search, Integer first, Integer max); boolean removeRole(RealmModel realm, RoleModel role); @@ -114,7 +144,9 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio /** * TODO: To be @deprecated Use the corresponding method from {@link ??RoleProvider}. */ - public Set getClientRoles(RealmModel realm, ClientModel client); + default public Stream getClientRolesStream(RealmModel realm, ClientModel client) { + return getClientRolesStream(realm, client, null, null); + } /** * TODO: To be @deprecated Use the corresponding method from {@link ??RoleProvider}. */ @@ -151,16 +183,11 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio return this.getClients(realm, null, null); } - /** - * @deprecated Use the corresponding method from {@link ClientProvider}. */ - @Override - public List getClients(RealmModel realm, Integer firstResult, Integer maxResults); - /** * @deprecated Use the corresponding method from {@link ClientProvider}. */ @Override default List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { - return searchClientsByClientId(realm, clientId, firstResult, maxResults); + return searchClientsByClientId(clientId, firstResult, maxResults, realm); } /** @@ -173,11 +200,6 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio @Override default ClientModel getClientById(String id, RealmModel realm) { return getClientById(realm, id); } - /** - * @deprecated Use the corresponding method from {@link ClientProvider}. */ - @Override - public List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults); - /** * @deprecated Use the corresponding method from {@link ClientProvider}. */ @Override @@ -196,10 +218,4 @@ public interface RealmProvider extends Provider /* TODO: Remove in future versio /** * @deprecated Use the corresponding method from {@link ClientProvider}. */ default boolean removeClient(String id, RealmModel realm) { return this.removeClient(realm, id); } - - /** - * @deprecated Use the corresponding method from {@link ClientProvider}. */ - @Override - public List getAlwaysDisplayInConsoleClients(RealmModel realm); - } diff --git a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java index f335d23552..435a3ce0e5 100755 --- a/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java +++ b/server-spi/src/main/java/org/keycloak/models/RoleContainerModel.java @@ -25,6 +25,8 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Bill Burke @@ -47,12 +49,30 @@ public interface RoleContainerModel { boolean removeRole(RoleModel role); - Set getRoles(); - - Set getRoles(Integer firstResult, Integer maxResults); + // TODO switch all usages to the stream variant + @Deprecated + default Set getRoles() { + return getRolesStream().collect(Collectors.toSet()); + } + + Stream getRolesStream(); + + // TODO switch all usages to the stream variant + @Deprecated + default Set getRoles(Integer firstResult, Integer maxResults) { + return getRolesStream(firstResult, maxResults).collect(Collectors.toSet()); + } + + Stream getRolesStream(Integer firstResult, Integer maxResults); + + // TODO switch all usages to the stream variant + @Deprecated + default Set searchForRoles(String search, Integer first, Integer max) { + return searchForRolesStream(search, first, max).collect(Collectors.toSet()); + } + + Stream searchForRolesStream(String search, Integer first, Integer max); - Set searchForRoles(String search, Integer first, Integer max); - List getDefaultRoles(); void addDefaultRole(String name); diff --git a/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java b/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java index d9e09c296d..2ba36e8908 100644 --- a/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/client/ClientLookupProvider.java @@ -20,6 +20,8 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Abstraction interface for lookoup of clients by id and clientId. These methods required for participating in login flows. @@ -71,10 +73,14 @@ public interface ClientLookupProvider { * @param clientId Searched substring of the public client * identifier ({@code client_id} in OIDC or {@code entityID} in SAML.) * @param firstResult First result to return. Ignored if negative or {@code null}. - * @param maxResults Maximim number of results to return. Ignored if negative or {@code null}. - * @return Model of the client, or {@code null} if no client is found. + * @param maxResults Maximum number of results to return. Ignored if negative or {@code null}. + * @return List of ClientModel or an empty list if no client is found. + * @deprecated Use {@link #searchClientsByClientIdStream(org.keycloak.models.RealmModel, java.lang.String, java.lang.Integer, java.lang.Integer)} instead. */ - List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults); + @Deprecated + default List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { + return searchClientsByClientIdStream(realm, clientId, firstResult, maxResults).collect(Collectors.toList()); + } /** * Case-insensitive search for clients that contain the given string in their public client identifier. @@ -82,11 +88,8 @@ public interface ClientLookupProvider { * @param clientId Searched substring of the public client * identifier ({@code client_id} in OIDC or {@code entityID} in SAML.) * @param firstResult First result to return. Ignored if negative or {@code null}. - * @param maxResults Maximim number of results to return. Ignored if negative or {@code null}. - * @return Models of the matching clients. Never returns {@code null}. - * @deprecated Use {@link #searchClientsByClientId(org.keycloak.models.RealmModel, java.lang.String, java.lang.Integer, java.lang.Integer)} instead. + * @param maxResults Maximum number of results to return. Ignored if negative or {@code null}. + * @return Stream of ClientModel or an empty stream if no client is found. */ - default List searchClientsByClientId(String clientId, Integer firstResult, Integer maxResults, RealmModel realm) { - return searchClientsByClientId(realm, clientId, firstResult, maxResults); - } + Stream searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults); } diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java index 3cb8d81c1a..939c47728f 100644 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java @@ -178,7 +178,9 @@ public class DefaultKeycloakSession implements KeycloakSession { @Override public ClientProvider clientStorageManager() { - if (clientStorageManager == null) clientStorageManager = new ClientStorageManager(this); + if (clientStorageManager == null) { + clientStorageManager = new ClientStorageManager(this, factory.getClientStorageProviderTimeout()); + } return clientStorageManager; } diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index 28f47147f1..1fe799cd8f 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -59,6 +59,12 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr // TODO: Likely should be changed to int and use Time.currentTime() to be compatible with all our "time" reps protected long serverStartupTimestamp; + /** + * Timeout is used as time boundary for obtaining clients from an external client storage. Default value is set + * to 3000 milliseconds and it's configurable. + */ + private long clientStorageProviderTimeout; + @Override public void register(ProviderEventListener listener) { listeners.add(listener); @@ -79,6 +85,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr public void init() { serverStartupTimestamp = System.currentTimeMillis(); + clientStorageProviderTimeout = Config.scope("client").getLong("storageProviderTimeout", 3000L); + ProviderManager pm = new ProviderManager(KeycloakDeploymentInfo.create().services(), getClass().getClassLoader(), Config.scope().getArray("providers")); spis.addAll(pm.loadSpis()); factoriesMap = loadFactories(pm); @@ -351,6 +359,10 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr return packageName.startsWith("org.keycloak") && !packageName.startsWith("org.keycloak.examples"); } + public long getClientStorageProviderTimeout() { + return clientStorageProviderTimeout; + } + /** * @return timestamp of Keycloak server startup */ diff --git a/services/src/main/java/org/keycloak/storage/ClientStorageManager.java b/services/src/main/java/org/keycloak/storage/ClientStorageManager.java index 70ca290577..2946ee184e 100644 --- a/services/src/main/java/org/keycloak/storage/ClientStorageManager.java +++ b/services/src/main/java/org/keycloak/storage/ClientStorageManager.java @@ -28,9 +28,11 @@ import org.keycloak.storage.client.ClientLookupProvider; import org.keycloak.storage.client.ClientStorageProvider; import org.keycloak.storage.client.ClientStorageProviderFactory; import org.keycloak.storage.client.ClientStorageProviderModel; +import org.keycloak.utils.ServicesUtils; import java.util.LinkedList; import java.util.List; +import java.util.stream.Stream; /** * @author Bill Burke @@ -41,6 +43,8 @@ public class ClientStorageManager implements ClientProvider { protected KeycloakSession session; + private long clientStorageProviderTimeout; + public static boolean isStorageProviderEnabled(RealmModel realm, String providerId) { ClientStorageProviderModel model = getStorageProviderModel(realm, providerId); return model.isEnabled(); @@ -118,8 +122,9 @@ public class ClientStorageManager implements ClientProvider { } - public ClientStorageManager(KeycloakSession session) { + public ClientStorageManager(KeycloakSession session, long clientStorageProviderTimeout) { this.session = session; + this.clientStorageProviderTimeout = clientStorageProviderTimeout; } @Override @@ -147,17 +152,22 @@ public class ClientStorageManager implements ClientProvider { return null; } + /** + * Obtaining clients from an external client storage is time-bounded. In case the external client storage + * isn't available at least clients from a local storage are returned. For this purpose + * the {@link org.keycloak.services.DefaultKeycloakSessionFactory#getClientStorageProviderTimeout()} property is used. + * Default value is 3000 milliseconds and it's configurable. + * See {@link org.keycloak.services.DefaultKeycloakSessionFactory} for details. + */ @Override - public List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { - List clients = session.clientLocalStorage().searchClientsByClientId(realm, clientId, firstResult, maxResults); - if (clients != null) { - return clients; - } - for (ClientLookupProvider provider : getEnabledStorageProviders(session, realm, ClientLookupProvider.class)) { - clients = provider.searchClientsByClientId(realm, clientId, firstResult, maxResults); - if (clients != null) return clients; - } - return null; + public Stream searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { + Stream local = session.clientLocalStorage().searchClientsByClientIdStream(realm, clientId, firstResult, maxResults); + Stream ext = getEnabledStorageProviders(session, realm, ClientLookupProvider.class).stream() + .flatMap(ServicesUtils.timeBound(session, + clientStorageProviderTimeout, + p -> ((ClientLookupProvider) p).searchClientsByClientIdStream(realm, clientId, firstResult, maxResults))); + + return Stream.concat(local, ext); } @Override @@ -171,13 +181,13 @@ public class ClientStorageManager implements ClientProvider { } @Override - public List getClients(RealmModel realm, Integer firstResult, Integer maxResults) { - return session.clientLocalStorage().getClients(realm, firstResult, maxResults); + public Stream getClientsStream(RealmModel realm, Integer firstResult, Integer maxResults) { + return session.clientLocalStorage().getClientsStream(realm, firstResult, maxResults); } @Override - public List getClients(RealmModel realm) { - return session.clientLocalStorage().getClients(realm); + public Stream getClientsStream(RealmModel realm) { + return session.clientLocalStorage().getClientsStream(realm); } @Override @@ -186,8 +196,8 @@ public class ClientStorageManager implements ClientProvider { } @Override - public List getAlwaysDisplayInConsoleClients(RealmModel realm) { - return session.clientLocalStorage().getAlwaysDisplayInConsoleClients(realm); + public Stream getAlwaysDisplayInConsoleClientsStream(RealmModel realm) { + return session.clientLocalStorage().getAlwaysDisplayInConsoleClientsStream(realm); } @Override diff --git a/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java b/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java index 2d274768af..43233b94a7 100644 --- a/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java +++ b/services/src/main/java/org/keycloak/storage/openshift/OpenshiftClientStorageProvider.java @@ -26,9 +26,8 @@ import org.keycloak.storage.StorageId; import org.keycloak.storage.client.ClientStorageProvider; import org.keycloak.storage.client.ClientStorageProviderModel; -import java.util.Collections; -import java.util.List; import java.util.regex.Matcher; +import java.util.stream.Stream; /** * @author Pedro Igor @@ -76,9 +75,9 @@ public class OpenshiftClientStorageProvider implements ClientStorageProvider { } @Override - public List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { + public Stream searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { // TODO not sure about this, but I don't see this implementation using the search now - return Collections.singletonList(getClientByClientId(realm, clientId)); + return Stream.of(getClientByClientId(realm, clientId)); } @Override diff --git a/services/src/main/java/org/keycloak/utils/ServicesUtils.java b/services/src/main/java/org/keycloak/utils/ServicesUtils.java new file mode 100644 index 0000000000..78825f1bfc --- /dev/null +++ b/services/src/main/java/org/keycloak/utils/ServicesUtils.java @@ -0,0 +1,56 @@ +/* + * Copyright 2020 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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. + */ + +package org.keycloak.utils; + +import org.jboss.logging.Logger; +import org.keycloak.executors.ExecutorsProvider; +import org.keycloak.models.KeycloakSession; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Utility class for general helper methods used across the keycloak-services. + */ +public class ServicesUtils { + + private static final Logger logger = Logger.getLogger(ServicesUtils.class); + + public static Function> timeBound(KeycloakSession session, + long timeout, + Function> func) { + ExecutorService executor = session.getProvider(ExecutorsProvider.class).getExecutor("storage-provider-threads"); + return p -> { + Callable> c = () -> func.apply(p); + Future> future = executor.submit(c); + try { + return future.get(timeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + future.cancel(true); + logger.debug("Function failed to return on time.", e); + return Stream.empty(); + } + }; + } +} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java index 0c7fdbc5fe..7cbd499655 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java @@ -36,7 +36,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -79,11 +78,11 @@ public class HardcodedClientStorageProvider implements ClientStorageProvider, Cl } @Override - public List searchClientsByClientId(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { + public Stream searchClientsByClientIdStream(RealmModel realm, String clientId, Integer firstResult, Integer maxResults) { if (clientId != null && this.clientId.toLowerCase().contains(clientId.toLowerCase())) { - return Collections.singletonList(new ClientAdapter(realm)); + return Stream.of(new ClientAdapter(realm)); } - return Collections.EMPTY_LIST; + return Stream.empty(); } public class ClientAdapter extends AbstractReadOnlyClientStorageAdapter {