From d2a8a95d799c846efb6989e945cb5e6769ae20e1 Mon Sep 17 00:00:00 2001 From: Michal Hajas Date: Tue, 8 Jun 2021 15:22:19 +0200 Subject: [PATCH] KEYCLOAK-18369 Create MapKeycloakTransaction interface --- .../MapPermissionTicketStore.java | 16 +- .../map/authorization/MapPolicyStore.java | 20 +- .../map/authorization/MapResourceStore.java | 20 +- .../map/authorization/MapScopeStore.java | 8 +- .../models/map/client/MapClientProvider.java | 12 +- .../clientscope/MapClientScopeProvider.java | 3 +- .../models/map/group/MapGroupProvider.java | 10 +- .../MapUserLoginFailureProvider.java | 4 +- .../models/map/realm/MapRealmProvider.java | 6 +- .../models/map/role/MapRoleProvider.java | 16 +- .../map/storage/MapKeycloakTransaction.java | 444 ++++-------------- .../ConcurrentHashMapKeycloakTransaction.java | 397 ++++++++++++++++ .../storage/chm/ConcurrentHashMapStorage.java | 6 +- .../UserSessionConcurrentHashMapStorage.java | 8 +- .../models/map/user/MapUserProvider.java | 34 +- .../userSession/MapUserSessionProvider.java | 28 +- 16 files changed, 574 insertions(+), 458 deletions(-) create mode 100644 model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java index 5c977554d0e..a607173ed2a 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPermissionTicketStore.java @@ -142,7 +142,7 @@ public class MapPermissionTicketStore> implements Permis public PermissionTicket findById(String id, String resourceServerId) { LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(PermissionTicket.SearchableFields.ID, Operator.EQ, id)) .findFirst() .map(this::entityToAdapter) @@ -153,7 +153,7 @@ public class MapPermissionTicketStore> implements Permis public List findByResourceServer(String resourceServerId) { LOG.tracef("findByResourceServer(%s)%s", resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)) + return tx.read(forResourceServer(resourceServerId)) .map(this::entityToAdapter) .collect(Collectors.toList()); } @@ -162,7 +162,7 @@ public class MapPermissionTicketStore> implements Permis public List findByOwner(String owner, String resourceServerId) { LOG.tracef("findByOwner(%s, %s)%s", owner, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.OWNER, Operator.EQ, owner)) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -172,7 +172,7 @@ public class MapPermissionTicketStore> implements Permis public List findByResource(String resourceId, String resourceServerId) { LOG.tracef("findByResource(%s, %s)%s", resourceId, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.RESOURCE_ID, Operator.EQ, resourceId)) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -182,7 +182,7 @@ public class MapPermissionTicketStore> implements Permis public List findByScope(String scopeId, String resourceServerId) { LOG.tracef("findByScope(%s, %s)%s", scopeId, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.SCOPE_ID, Operator.EQ, scopeId)) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -213,7 +213,7 @@ public class MapPermissionTicketStore> implements Permis ); Comparator> c = Comparator.comparing(MapPermissionTicketEntity::getId); - return paginatedStream(tx.getUpdatedNotRemoved(mcb) + return paginatedStream(tx.read(mcb) .sorted(c), firstResult, maxResult) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -297,7 +297,7 @@ public class MapPermissionTicketStore> implements Permis .findById(ticket.getResourceId(), ticket.getResourceServerId()); } - return paginatedStream(tx.getUpdatedNotRemoved(mcb) + return paginatedStream(tx.read(mcb) .filter(distinctByKey(MapPermissionTicketEntity::getResourceId)) .sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID) .map(ticketResourceMapper) @@ -310,7 +310,7 @@ public class MapPermissionTicketStore> implements Permis ModelCriteriaBuilder mcb = permissionTicketStore.createCriteriaBuilder() .compare(SearchableFields.OWNER, Operator.EQ, owner); - return paginatedStream(tx.getUpdatedNotRemoved(mcb) + return paginatedStream(tx.read(mcb) .filter(distinctByKey(MapPermissionTicketEntity::getResourceId)) .sorted(MapPermissionTicketEntity.COMPARE_BY_RESOURCE_ID), first, max) .map(ticket -> authorizationProvider.getStoreFactory().getResourceStore() diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java index 51d91725425..162757b0821 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapPolicyStore.java @@ -111,7 +111,7 @@ public class MapPolicyStore implements PolicyStore { public Policy findById(String id, String resourceServerId) { LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.ID, Operator.EQ, id)) .findFirst() .map(this::entityToAdapter) @@ -122,7 +122,7 @@ public class MapPolicyStore implements PolicyStore { public Policy findByName(String name, String resourceServerId) { LOG.tracef("findByName(%s, %s)%s", name, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.NAME, Operator.EQ, name)) .findFirst() .map(this::entityToAdapter) @@ -133,7 +133,7 @@ public class MapPolicyStore implements PolicyStore { public List findByResourceServer(String id) { LOG.tracef("findByResourceServer(%s)%s", id, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(id)) + return tx.read(forResourceServer(id)) .map(this::entityToAdapter) .collect(Collectors.toList()); } @@ -153,7 +153,7 @@ public class MapPolicyStore implements PolicyStore { mcb = mcb.compare(SearchableFields.OWNER, Operator.NOT_EXISTS); } - return paginatedStream(tx.getUpdatedNotRemoved(mcb) + return paginatedStream(tx.read(mcb) .sorted(MapPolicyEntity.COMPARE_BY_NAME), firstResult, maxResult) .map(MapPolicyEntity::getId) .map(K::toString) @@ -205,7 +205,7 @@ public class MapPolicyStore implements PolicyStore { public void findByResource(String resourceId, String resourceServerId, Consumer consumer) { LOG.tracef("findByResource(%s, %s, %s)%s", resourceId, resourceServerId, consumer, getShortStackTrace()); - tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + tx.read(forResourceServer(resourceServerId) .compare(Policy.SearchableFields.RESOURCE_ID, Operator.EQ, resourceId)) .map(this::entityToAdapter) .forEach(consumer); @@ -213,7 +213,7 @@ public class MapPolicyStore implements PolicyStore { @Override public void findByResourceType(String type, String resourceServerId, Consumer policyConsumer) { - tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.CONFIG, Operator.LIKE, (Object[]) new String[] {"defaultResourceType", type})) .map(this::entityToAdapter) .forEach(policyConsumer); @@ -221,7 +221,7 @@ public class MapPolicyStore implements PolicyStore { @Override public List findByScopeIds(List scopeIds, String resourceServerId) { - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.SCOPE_ID, Operator.IN, scopeIds)) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -241,12 +241,12 @@ public class MapPolicyStore implements PolicyStore { .compare(SearchableFields.CONFIG, Operator.NOT_EXISTS, (Object[]) new String[] {"defaultResourceType"}); } - tx.getUpdatedNotRemoved(mcb).map(this::entityToAdapter).forEach(consumer); + tx.read(mcb).map(this::entityToAdapter).forEach(consumer); } @Override public List findByType(String type, String resourceServerId) { - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.TYPE, Operator.EQ, type)) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -254,7 +254,7 @@ public class MapPolicyStore implements PolicyStore { @Override public List findDependentPolicies(String id, String resourceServerId) { - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.ASSOCIATED_POLICY_ID, Operator.EQ, id)) .map(this::entityToAdapter) .collect(Collectors.toList()); diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java index e7882243723..3ff6e855080 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapResourceStore.java @@ -113,7 +113,7 @@ public class MapResourceStore> implements ResourceStore public Resource findById(String id, String resourceServerId) { LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.ID, Operator.EQ, id)) .findFirst() .map(this::entityToAdapter) @@ -128,7 +128,7 @@ public class MapResourceStore> implements ResourceStore private void findByOwnerFilter(String ownerId, String resourceServerId, Consumer consumer, int firstResult, int maxResult) { LOG.tracef("findByOwnerFilter(%s, %s, %s, %d, %d)%s", ownerId, resourceServerId, consumer, firstResult, maxResult, getShortStackTrace()); Comparator> c = Comparator.comparing(MapResourceEntity::getId); - paginatedStream(tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + paginatedStream(tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.OWNER, Operator.EQ, ownerId)) .sorted(c), firstResult, maxResult) .map(this::entityToAdapter) @@ -148,7 +148,7 @@ public class MapResourceStore> implements ResourceStore public List findByUri(String uri, String resourceServerId) { LOG.tracef("findByUri(%s, %s)%s", uri, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.URI, Operator.EQ, uri)) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -158,7 +158,7 @@ public class MapResourceStore> implements ResourceStore public List findByResourceServer(String resourceServerId) { LOG.tracef("findByResourceServer(%s)%s", resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId)) + return tx.read(forResourceServer(resourceServerId)) .map(this::entityToAdapter) .collect(Collectors.toList()); } @@ -172,7 +172,7 @@ public class MapResourceStore> implements ResourceStore .toArray(ModelCriteriaBuilder[]::new) ); - return paginatedStream(tx.getUpdatedNotRemoved(mcb) + return paginatedStream(tx.read(mcb) .sorted(MapResourceEntity.COMPARE_BY_NAME), firstResult, maxResult) .map(this::entityToAdapter) .collect(Collectors.toList()); @@ -210,7 +210,7 @@ public class MapResourceStore> implements ResourceStore public void findByScope(List scopes, String resourceServerId, Consumer consumer) { LOG.tracef("findByScope(%s, %s, %s)%s", scopes, resourceServerId, consumer, getShortStackTrace()); - tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.SCOPE_ID, Operator.IN, scopes)) .map(this::entityToAdapter) .forEach(consumer); @@ -224,7 +224,7 @@ public class MapResourceStore> implements ResourceStore @Override public Resource findByName(String name, String ownerId, String resourceServerId) { LOG.tracef("findByName(%s, %s, %s)%s", name, ownerId, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.OWNER, Operator.EQ, ownerId) .compare(SearchableFields.NAME, Operator.EQ, name)) .findFirst() @@ -235,7 +235,7 @@ public class MapResourceStore> implements ResourceStore @Override public void findByType(String type, String resourceServerId, Consumer consumer) { LOG.tracef("findByType(%s, %s, %s)%s", type, resourceServerId, consumer, getShortStackTrace()); - tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.TYPE, Operator.EQ, type)) .map(this::entityToAdapter) .forEach(consumer); @@ -252,7 +252,7 @@ public class MapResourceStore> implements ResourceStore mcb = mcb.compare(SearchableFields.OWNER, Operator.EQ, owner); } - tx.getUpdatedNotRemoved(mcb) + tx.read(mcb) .map(this::entityToAdapter) .forEach(consumer); } @@ -260,7 +260,7 @@ public class MapResourceStore> implements ResourceStore @Override public void findByTypeInstance(String type, String resourceServerId, Consumer consumer) { LOG.tracef("findByTypeInstance(%s, %s, %s)%s", type, resourceServerId, consumer, getShortStackTrace()); - tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + tx.read(forResourceServer(resourceServerId) .compare(SearchableFields.OWNER, Operator.NE, resourceServerId) .compare(SearchableFields.TYPE, Operator.EQ, type)) .map(this::entityToAdapter) diff --git a/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java b/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java index 3c9c184abdc..d715290ac7e 100644 --- a/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java +++ b/model/map/src/main/java/org/keycloak/models/map/authorization/MapScopeStore.java @@ -109,7 +109,7 @@ public class MapScopeStore implements ScopeStore { public Scope findById(String id, String resourceServerId) { LOG.tracef("findById(%s, %s)%s", id, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId) + return tx.read(forResourceServer(resourceServerId) .compare(Scope.SearchableFields.ID, Operator.EQ, id)) .findFirst() .map(this::entityToAdapter) @@ -120,7 +120,7 @@ public class MapScopeStore implements ScopeStore { public Scope findByName(String name, String resourceServerId) { LOG.tracef("findByName(%s, %s)%s", name, resourceServerId, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(resourceServerId).compare(Scope.SearchableFields.NAME, + return tx.read(forResourceServer(resourceServerId).compare(Scope.SearchableFields.NAME, Operator.EQ, name)) .findFirst() .map(this::entityToAdapter) @@ -131,7 +131,7 @@ public class MapScopeStore implements ScopeStore { public List findByResourceServer(String id) { LOG.tracef("findByResourceServer(%s)%s", id, getShortStackTrace()); - return tx.getUpdatedNotRemoved(forResourceServer(id)) + return tx.read(forResourceServer(id)) .map(this::entityToAdapter) .collect(Collectors.toList()); } @@ -155,7 +155,7 @@ public class MapScopeStore implements ScopeStore { } } - return paginatedStream(tx.getUpdatedNotRemoved(mcb).map(this::entityToAdapter), firstResult, maxResult) + return paginatedStream(tx.read(mcb).map(this::entityToAdapter), firstResult, maxResult) .collect(Collectors.toList()); } } 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 bea3ea716ca..a43ab924d42 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 @@ -134,7 +134,7 @@ public class MapClientProvider implements ClientProvider { ModelCriteriaBuilder mcb = clientStore.createCriteriaBuilder() .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .sorted(COMPARE_BY_CLIENT_ID) .map(entityToAdapterFunc(realm)) ; @@ -248,7 +248,7 @@ public class MapClientProvider implements ClientProvider { .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.CLIENT_ID, Operator.ILIKE, clientId); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .map(entityToAdapterFunc(realm)) .findFirst() .orElse(null) @@ -265,7 +265,7 @@ public class MapClientProvider implements ClientProvider { .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.CLIENT_ID, Operator.ILIKE, "%" + clientId + "%"); - Stream> s = tx.getUpdatedNotRemoved(mcb) + Stream> s = tx.read(mcb) .sorted(COMPARE_BY_CLIENT_ID); return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm)); @@ -280,7 +280,7 @@ public class MapClientProvider implements ClientProvider { mcb = mcb.compare(SearchableFields.ATTRIBUTE, Operator.EQ, entry.getKey(), entry.getValue()); } - Stream> s = tx.getUpdatedNotRemoved(mcb) + Stream> s = tx.read(mcb) .sorted(COMPARE_BY_CLIENT_ID); return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm)); @@ -344,7 +344,7 @@ public class MapClientProvider implements ClientProvider { .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ENABLED, Operator.EQ, Boolean.TRUE); - try (Stream> st = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> st = tx.read(mcb)) { return st .filter(mce -> mce.getRedirectUris() != null && ! mce.getRedirectUris().isEmpty()) .collect(Collectors.toMap( @@ -358,7 +358,7 @@ public class MapClientProvider implements ClientProvider { ModelCriteriaBuilder mcb = clientStore.createCriteriaBuilder() .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.SCOPE_MAPPING_ROLE, Operator.EQ, role.getId()); - try (Stream> toRemove = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> toRemove = tx.read(mcb)) { toRemove .map(clientEntity -> session.clients().getClientById(realm, clientEntity.getId().toString())) .filter(Objects::nonNull) diff --git a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java index 35d16766346..0c5fa6d0481 100644 --- a/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/clientscope/MapClientScopeProvider.java @@ -30,7 +30,6 @@ import org.keycloak.models.ClientScopeModel; import org.keycloak.models.ClientScopeProvider; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ModelDuplicateException; -import org.keycloak.models.ModelException; import org.keycloak.models.RealmModel; import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapStorage; @@ -80,7 +79,7 @@ public class MapClientScopeProvider implements ClientScopeProvider { ModelCriteriaBuilder mcb = clientScopeStore.createCriteriaBuilder() .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .sorted(COMPARE_BY_NAME) .map(entityToAdapterFunc(realm)); } diff --git a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java index a7d1840c684..f04be591df9 100644 --- a/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/group/MapGroupProvider.java @@ -100,7 +100,7 @@ public class MapGroupProvider implements GroupProvider { mcb = modifier.apply(mcb); } - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .map(entityToAdapterFunc(realm)) .sorted(GroupModel.COMPARE_BY_NAME) ; @@ -116,7 +116,7 @@ public class MapGroupProvider implements GroupProvider { mcb = mcb.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%"); } - Stream groupModelStream = tx.getUpdatedNotRemoved(mcb) + Stream groupModelStream = tx.read(mcb) .map(entityToAdapterFunc(realm)) .sorted(Comparator.comparing(GroupModel::getName)); @@ -261,7 +261,7 @@ public class MapGroupProvider implements GroupProvider { .compare(SearchableFields.PARENT_ID, Operator.EQ, parentId) .compare(SearchableFields.NAME, Operator.EQ, group.getName()); - try (Stream> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> possibleSiblings = tx.read(mcb)) { if (possibleSiblings.findAny().isPresent()) { throw new ModelDuplicateException("Parent already contains subgroup named '" + group.getName() + "'"); } @@ -283,7 +283,7 @@ public class MapGroupProvider implements GroupProvider { .compare(SearchableFields.PARENT_ID, Operator.EQ, (Object) null) .compare(SearchableFields.NAME, Operator.EQ, subGroup.getName()); - try (Stream> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> possibleSiblings = tx.read(mcb)) { if (possibleSiblings.findAny().isPresent()) { throw new ModelDuplicateException("There is already a top level group named '" + subGroup.getName() + "'"); } @@ -297,7 +297,7 @@ public class MapGroupProvider implements GroupProvider { ModelCriteriaBuilder mcb = groupStore.createCriteriaBuilder() .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId()); - try (Stream> toRemove = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> toRemove = tx.read(mcb)) { toRemove .map(groupEntity -> session.groups().getGroupById(realm, groupEntity.getId().toString())) .forEach(groupModel -> groupModel.deleteRoleMapping(role)); diff --git a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java index 2cb229a8eea..5ab8acc7894 100644 --- a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureProvider.java @@ -66,7 +66,7 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider LOG.tracef("getUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace()); - return userLoginFailureTx.getUpdatedNotRemoved(mcb) + return userLoginFailureTx.read(mcb) .findFirst() .map(userLoginFailureEntityToAdapterFunc(realm)) .orElse(null); @@ -80,7 +80,7 @@ public class MapUserLoginFailureProvider implements UserLoginFailureProvider LOG.tracef("addUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace()); - MapUserLoginFailureEntity userLoginFailureEntity = userLoginFailureTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null); + MapUserLoginFailureEntity userLoginFailureEntity = userLoginFailureTx.read(mcb).findFirst().orElse(null); if (userLoginFailureEntity == null) { userLoginFailureEntity = new MapUserLoginFailureEntity<>(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), realm.getId(), userId); diff --git a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java index d416bdd22f1..37391f3eaa5 100644 --- a/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/realm/MapRealmProvider.java @@ -110,7 +110,7 @@ public class MapRealmProvider implements RealmProvider { ModelCriteriaBuilder mcb = realmStore.createCriteriaBuilder() .compare(SearchableFields.NAME, Operator.EQ, name); - K realmId = tx.getUpdatedNotRemoved(mcb) + K realmId = tx.read(mcb) .findFirst() .map(MapRealmEntity::getId) .orElse(null); @@ -132,7 +132,7 @@ public class MapRealmProvider implements RealmProvider { } private Stream getRealmsStream(ModelCriteriaBuilder mcb) { - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .map(this::entityToAdapter) .sorted(RealmModel.COMPARE_BY_NAME); } @@ -174,7 +174,7 @@ public class MapRealmProvider implements RealmProvider { ModelCriteriaBuilder mcb = realmStore.createCriteriaBuilder() .compare(SearchableFields.CLIENT_INITIAL_ACCESS, Operator.EXISTS); - tx.getUpdatedNotRemoved(mcb) + tx.read(mcb) .map(e -> registerEntityForChanges(tx, e)) .forEach(MapRealmEntity::removeExpiredClientInitialAccesses); } diff --git a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java index 1c6b29a7b26..471a56fa9b8 100644 --- a/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/role/MapRoleProvider.java @@ -110,7 +110,7 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.IS_CLIENT_ROLE, Operator.NE, true); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .sorted(COMPARE_BY_NAME) .map(entityToAdapterFunc(realm)); } @@ -147,7 +147,7 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.REALM_ID, Operator.EQ, client.getRealm().getId()) .compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId()); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .sorted(COMPARE_BY_NAME) .map(entityToAdapterFunc(client.getRealm())); } @@ -165,7 +165,7 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false); //remove role from realm-roles composites - try (Stream> baseStream = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> baseStream = tx.read(mcb)) { StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles) .filter(pair -> role.getId().equals(pair.getV())) @@ -189,7 +189,7 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId()) .compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false); - try (Stream> baseStream = tx.getUpdatedNotRemoved(mcbClient)) { + try (Stream> baseStream = tx.read(mcbClient)) { StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity::getCompositeRoles) .filter(pair -> role.getId().equals(pair.getV())) @@ -246,7 +246,7 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.NAME, Operator.ILIKE, name); - String roleId = tx.getUpdatedNotRemoved(mcb) + String roleId = tx.read(mcb) .map(entityToAdapterFunc(realm)) .map(RoleModel::getId) .findFirst() @@ -267,7 +267,7 @@ public class MapRoleProvider implements RoleProvider { .compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId()) .compare(SearchableFields.NAME, Operator.ILIKE, name); - String roleId = tx.getUpdatedNotRemoved(mcb) + String roleId = tx.read(mcb) .map(entityToAdapterFunc(client.getRealm())) .map(RoleModel::getId) .findFirst() @@ -303,7 +303,7 @@ public class MapRoleProvider implements RoleProvider { roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%") ); - Stream> s = tx.getUpdatedNotRemoved(mcb) + Stream> s = tx.read(mcb) .sorted(COMPARE_BY_NAME); return paginatedStream(s.map(entityToAdapterFunc(realm)), first, max); @@ -321,7 +321,7 @@ public class MapRoleProvider implements RoleProvider { roleStore.createCriteriaBuilder().compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%"), roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%") ); - Stream> s = tx.getUpdatedNotRemoved(mcb) + Stream> s = tx.read(mcb) .sorted(COMPARE_BY_NAME); return paginatedStream(s,first, max).map(entityToAdapterFunc(client.getRealm())); diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java b/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java index f4954b2d49f..2215c1c130b 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/MapKeycloakTransaction.java @@ -17,389 +17,109 @@ package org.keycloak.models.map.storage; import org.keycloak.models.KeycloakTransaction; - import org.keycloak.models.map.common.AbstractEntity; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; + import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; import java.util.stream.Stream; -import org.jboss.logging.Logger; -public class MapKeycloakTransaction, M> implements KeycloakTransaction { - - private final static Logger log = Logger.getLogger(MapKeycloakTransaction.class); - - private enum MapOperation { - CREATE, UPDATE, DELETE, - } - - private boolean active; - private boolean rollback; - private final Map tasks = new LinkedHashMap<>(); - private final MapStorage map; - - public MapKeycloakTransaction(MapStorage map) { - this.map = map; - } - - @Override - public void begin() { - active = true; - } - - @Override - public void commit() { - log.tracef("Commit - %s", map); - - if (rollback) { - throw new RuntimeException("Rollback only!"); - } - - for (MapTaskWithValue value : tasks.values()) { - value.execute(); - } - } - - @Override - public void rollback() { - tasks.clear(); - } - - @Override - public void setRollbackOnly() { - rollback = true; - } - - @Override - public boolean getRollbackOnly() { - return rollback; - } - - @Override - public boolean isActive() { - return active; - } +public interface MapKeycloakTransaction, M> extends KeycloakTransaction { /** - * Adds a given task if not exists for the given key - */ - protected void addTask(K key, MapTaskWithValue task) { - log.tracef("Adding operation %s for %s @ %08x", task.getOperation(), key, System.identityHashCode(task.getValue())); - - K taskKey = key; - tasks.merge(taskKey, task, MapTaskCompose::new); - } - - // This is for possibility to lookup for session by id, which was created in this transaction - public V read(K key) { - try { // TODO: Consider using Optional rather than handling NPE - return read(key, map::read); - } catch (NullPointerException ex) { - return null; - } - } - - public V read(K key, Function defaultValueFunc) { - MapTaskWithValue current = tasks.get(key); - // If the key exists, then it has entered the "tasks" after bulk delete that could have - // removed it, so looking through bulk deletes is irrelevant - if (tasks.containsKey(key)) { - return current.getValue(); - } - - // If the key does not exist, then it would be read fresh from the storage, but then it - // could have been removed by some bulk delete in the existing tasks. Check it. - final V value = defaultValueFunc.apply(key); - for (MapTaskWithValue val : tasks.values()) { - if (val instanceof MapKeycloakTransaction.BulkDeleteOperation) { - final BulkDeleteOperation delOp = (BulkDeleteOperation) val; - if (! delOp.getFilterForNonDeletedObjects().test(value)) { - return null; - } - } - } - - return value; - } - - /** - * Returns the stream of records that match given criteria and includes changes made in this transaction, i.e. - * the result contains updates and excludes records that have been deleted in this transaction. + * Instructs this transaction to add a new value into the underlying store on commit. * - * @param mcb - * @return + * @param key an identifier that will be associated with the {@code value} + * @param value the value */ - public Stream getUpdatedNotRemoved(ModelCriteriaBuilder mcb) { - Predicate filterOutAllBulkDeletedObjects = tasks.values().stream() - .filter(BulkDeleteOperation.class::isInstance) - .map(BulkDeleteOperation.class::cast) - .map(BulkDeleteOperation::getFilterForNonDeletedObjects) - .reduce(Predicate::and) - .orElse(v -> true); - - Stream updatedAndNotRemovedObjectsStream = this.map.read(mcb) - .filter(filterOutAllBulkDeletedObjects) - .map(this::getUpdated) // If the object has been removed, tx.get will return null, otherwise it will return me.getValue() - .filter(Objects::nonNull); - - // In case of created values stored in MapKeycloakTransaction, we need filter those according to the filter - MapModelCriteriaBuilder mapMcb = mcb.unwrap(MapModelCriteriaBuilder.class); - Stream res = mapMcb == null - ? updatedAndNotRemovedObjectsStream - : Stream.concat( - createdValuesStream(mapMcb.getKeyFilter(), mapMcb.getEntityFilter()), - updatedAndNotRemovedObjectsStream - ); - - return res; - } + void create(K key, V value); /** - * Returns the stream of records that match given criteria and includes changes made in this transaction, i.e. - * the result contains updates and excludes records that have been deleted in this transaction. + * Provides possibility to lookup for values by a {@code key} in the underlying store with respect to changes done + * in current transaction. * - * @param mcb - * @return + * @param key identifier of a value + * @return a value associated with the given {@code key} */ - public long getCount(ModelCriteriaBuilder mcb) { - return getUpdatedNotRemoved(mcb).count(); - } + V read(K key); /** - * Returns a updated version of the {@code orig} object as updated in this transaction. + * Looks up a value in the current transaction with corresponding key, returns {@code defaultValueFunc} when + * the transaction does not contain a value for the {@code key} identifier. + * + * @param key identifier of a value + * @param defaultValueFunc fallback function if the transaction does not contain a value that corresponds to {@code key} + * @return a value associated with the given {@code key}, or the result of {@code defaultValueFunc} + * + */ + V read(K key, Function defaultValueFunc); + + /** + * Returns a stream of values from underlying storage that are updated based on the current transaction changes; + * i.e. the result contains updates and excludes of records that have been created, updated or deleted in this + * transaction by methods {@link MapKeycloakTransaction#create}, {@link MapKeycloakTransaction#update}, + * {@link MapKeycloakTransaction#delete}, etc. + * + * @param mcb criteria to filter values + * @return values that fulfill the given criteria, that are updated based on changes in the current transaction + */ + Stream read(ModelCriteriaBuilder mcb); + + + /** + * Returns a number of values present in the underlying storage that fulfill the given criteria with respect to + * changes done in the current transaction. + * + * @param mcb criteria to filter values + * @return number of values present in the storage that fulfill the given criteria + */ + long getCount(ModelCriteriaBuilder mcb); + + /** + * Instructs this transaction to force-update the {@code value} associated with the identifier {@code key} in the + * underlying store on commit. + * + * @param key identifier of the {@code value} + * @param value updated version of the value + */ + void update(K key, V value); + + /** + * Returns an updated version of the {@code orig} object as updated in this transaction. + * * If the underlying store handles transactions on its own, this can return {@code orig} directly. - * @param orig - * @return The {@code orig} object as visible from this transaction, or {@code null} if the object has been removed. + * + * @param orig possibly stale version of some object from the underlying store + * @return the {@code orig} object as visible from this transaction, or {@code null} if the object has been removed. */ - public V getUpdated(V orig) { - MapTaskWithValue current = orig == null ? null : tasks.get(orig.getId()); - return current == null ? orig : current.getValue(); - } - - public void update(K key, V value) { - addTask(key, new UpdateOperation(key, value)); - } - - public void create(K key, V value) { - addTask(key, new CreateOperation(key, value)); - } - - public void updateIfChanged(K key, V value, Predicate shouldPut) { - log.tracef("Adding operation UPDATE_IF_CHANGED for %s @ %08x", key, System.identityHashCode(value)); - - K taskKey = key; - MapTaskWithValue op = new MapTaskWithValue(value) { - @Override - public void execute() { - if (shouldPut.test(getValue())) { - map.update(key, getValue()); - } - } - @Override public MapOperation getOperation() { return MapOperation.UPDATE; } - }; - tasks.merge(taskKey, op, this::merge); - } - - public void delete(K key) { - addTask(key, new DeleteOperation(key)); + default V getUpdated(V orig) { + return orig; } /** - * Bulk removal of items. - * - * @param artificialKey Key to record the transaction with, must be a key that does not exist in this transaction to - * prevent collision with other operations in this transaction - * @param mcb + * Instructs this transaction to update the {@code value} associated with the identifier {@code key} in the + * underlying store on commit, if by the time of {@code commit} the {@code shouldPut} predicate returns {@code true} + * + * @param key identifier of the {@code value} + * @param value new version of the value + * @param shouldPut predicate to check in commit phase */ - public long delete(K artificialKey, ModelCriteriaBuilder mcb) { - log.tracef("Adding operation DELETE_BULK"); + void updateIfChanged(K key, V value, Predicate shouldPut); - // Remove all tasks that create / update / delete objects deleted by the bulk removal. - final BulkDeleteOperation bdo = new BulkDeleteOperation(mcb); - Predicate filterForNonDeletedObjects = bdo.getFilterForNonDeletedObjects(); - long res = 0; - for (Iterator> it = tasks.entrySet().iterator(); it.hasNext();) { - Entry me = it.next(); - if (! filterForNonDeletedObjects.test(me.getValue().getValue())) { - log.tracef(" [DELETE_BULK] removing %s", me.getKey()); - it.remove(); - res++; - } - } + /** + * Instructs this transaction to delete a value associated with the identifier {@code key} from the underlying store + * on commit. + * + * @param key identifier of a value + */ + void delete(K key); - tasks.put(artificialKey, bdo); + /** + * Instructs this transaction to remove values (identified by {@code mcb} filter) from the underlying store on commit. + * + * @param artificialKey key to record the transaction with, must be a key that does not exist in this transaction to + * prevent collisions with other operations in this transaction + * @param mcb criteria to delete values + */ + long delete(K artificialKey, ModelCriteriaBuilder mcb); - return res + bdo.getCount(); - } - - private Stream createdValuesStream(Predicate keyFilter, Predicate entityFilter) { - return this.tasks.entrySet().stream() - .filter(me -> keyFilter.test(me.getKey())) - .map(Map.Entry::getValue) - .filter(v -> v.containsCreate() && ! v.isReplace()) - .map(MapTaskWithValue::getValue) - .filter(Objects::nonNull) - .filter(entityFilter) - // make a snapshot - .collect(Collectors.toList()).stream(); - } - - private MapTaskWithValue merge(MapTaskWithValue oldValue, MapTaskWithValue newValue) { - switch (newValue.getOperation()) { - case DELETE: - return oldValue.containsCreate() ? null : newValue; - default: - return new MapTaskCompose(oldValue, newValue); - } - } - - protected abstract class MapTaskWithValue { - protected final V value; - - public MapTaskWithValue(V value) { - this.value = value; - } - - public V getValue() { - return value; - } - - public boolean containsCreate() { - return MapOperation.CREATE == getOperation(); - } - - public boolean containsRemove() { - return MapOperation.DELETE == getOperation(); - } - - public boolean isReplace() { - return false; - } - - public abstract MapOperation getOperation(); - public abstract void execute(); - } - - private class MapTaskCompose extends MapTaskWithValue { - - private final MapTaskWithValue oldValue; - private final MapTaskWithValue newValue; - - public MapTaskCompose(MapTaskWithValue oldValue, MapTaskWithValue newValue) { - super(null); - this.oldValue = oldValue; - this.newValue = newValue; - } - - @Override - public void execute() { - oldValue.execute(); - newValue.execute(); - } - - @Override - public V getValue() { - return newValue.getValue(); - } - - @Override - public MapOperation getOperation() { - return null; - } - - @Override - public boolean containsCreate() { - return oldValue.containsCreate() || newValue.containsCreate(); - } - - @Override - public boolean containsRemove() { - return oldValue.containsRemove() || newValue.containsRemove(); - } - - @Override - public boolean isReplace() { - return (newValue.getOperation() == MapOperation.CREATE && oldValue.containsRemove()) || - (oldValue instanceof MapKeycloakTransaction.MapTaskCompose && ((MapTaskCompose) oldValue).isReplace()); - } - } - - private class CreateOperation extends MapTaskWithValue { - private final K key; - - public CreateOperation(K key, V value) { - super(value); - this.key = key; - } - - @Override public void execute() { map.create(key, getValue()); } - @Override public MapOperation getOperation() { return MapOperation.CREATE; } - } - - private class UpdateOperation extends MapTaskWithValue { - private final K key; - - public UpdateOperation(K key, V value) { - super(value); - this.key = key; - } - - @Override public void execute() { map.update(key, getValue()); } - @Override public MapOperation getOperation() { return MapOperation.UPDATE; } - } - - private class DeleteOperation extends MapTaskWithValue { - private final K key; - - public DeleteOperation(K key) { - super(null); - this.key = key; - } - - @Override public void execute() { map.delete(key); } - @Override public MapOperation getOperation() { return MapOperation.DELETE; } - } - - private class BulkDeleteOperation extends MapTaskWithValue { - - private final ModelCriteriaBuilder mcb; - - public BulkDeleteOperation(ModelCriteriaBuilder mcb) { - super(null); - this.mcb = mcb; - } - - @Override - @SuppressWarnings("unchecked") - public void execute() { - map.delete(mcb); - } - - public Predicate getFilterForNonDeletedObjects() { - if (! (mcb instanceof MapModelCriteriaBuilder)) { - return t -> true; - } - - @SuppressWarnings("unchecked") - final MapModelCriteriaBuilder mmcb = (MapModelCriteriaBuilder) mcb; - - Predicate entityFilter = mmcb.getEntityFilter(); - Predicate keyFilter = ((MapModelCriteriaBuilder) mcb).getKeyFilter(); - return v -> v == null || ! (keyFilter.test(v.getId()) && entityFilter.test(v)); - } - - @Override - public MapOperation getOperation() { - return MapOperation.DELETE; - } - - private long getCount() { - return map.getCount(mcb); - } - } -} \ No newline at end of file +} diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java new file mode 100644 index 00000000000..9caa528c56c --- /dev/null +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapKeycloakTransaction.java @@ -0,0 +1,397 @@ +/* + * 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.models.map.storage.chm; + +import org.keycloak.models.map.common.AbstractEntity; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.jboss.logging.Logger; +import org.keycloak.models.map.storage.MapKeycloakTransaction; +import org.keycloak.models.map.storage.MapModelCriteriaBuilder; +import org.keycloak.models.map.storage.MapStorage; +import org.keycloak.models.map.storage.ModelCriteriaBuilder; + +public class ConcurrentHashMapKeycloakTransaction, M> implements MapKeycloakTransaction { + + private final static Logger log = Logger.getLogger(ConcurrentHashMapKeycloakTransaction.class); + + private boolean active; + private boolean rollback; + private final Map tasks = new LinkedHashMap<>(); + private final MapStorage map; + + enum MapOperation { + CREATE, UPDATE, DELETE, + } + + public ConcurrentHashMapKeycloakTransaction(MapStorage map) { + this.map = map; + } + + @Override + public void begin() { + active = true; + } + + @Override + public void commit() { + log.tracef("Commit - %s", map); + + if (rollback) { + throw new RuntimeException("Rollback only!"); + } + + for (MapTaskWithValue value : tasks.values()) { + value.execute(); + } + } + + @Override + public void rollback() { + tasks.clear(); + } + + @Override + public void setRollbackOnly() { + rollback = true; + } + + @Override + public boolean getRollbackOnly() { + return rollback; + } + + @Override + public boolean isActive() { + return active; + } + + /** + * Adds a given task if not exists for the given key + */ + protected void addTask(K key, MapTaskWithValue task) { + log.tracef("Adding operation %s for %s @ %08x", task.getOperation(), key, System.identityHashCode(task.getValue())); + + K taskKey = key; + tasks.merge(taskKey, task, MapTaskCompose::new); + } + + @Override + public V read(K key) { + try { // TODO: Consider using Optional rather than handling NPE + return read(key, map::read); + } catch (NullPointerException ex) { + return null; + } + } + + @Override + public V read(K key, Function defaultValueFunc) { + MapTaskWithValue current = tasks.get(key); + // If the key exists, then it has entered the "tasks" after bulk delete that could have + // removed it, so looking through bulk deletes is irrelevant + if (tasks.containsKey(key)) { + return current.getValue(); + } + + // If the key does not exist, then it would be read fresh from the storage, but then it + // could have been removed by some bulk delete in the existing tasks. Check it. + final V value = defaultValueFunc.apply(key); + for (MapTaskWithValue val : tasks.values()) { + if (val instanceof ConcurrentHashMapKeycloakTransaction.BulkDeleteOperation) { + final BulkDeleteOperation delOp = (BulkDeleteOperation) val; + if (! delOp.getFilterForNonDeletedObjects().test(value)) { + return null; + } + } + } + + return value; + } + + /** + * Returns the stream of records that match given criteria and includes changes made in this transaction, i.e. + * the result contains updates and excludes records that have been deleted in this transaction. + * + * @param mcb + * @return + */ + @Override + public Stream read(ModelCriteriaBuilder mcb) { + Predicate filterOutAllBulkDeletedObjects = tasks.values().stream() + .filter(BulkDeleteOperation.class::isInstance) + .map(BulkDeleteOperation.class::cast) + .map(BulkDeleteOperation::getFilterForNonDeletedObjects) + .reduce(Predicate::and) + .orElse(v -> true); + + Stream updatedAndNotRemovedObjectsStream = this.map.read(mcb) + .filter(filterOutAllBulkDeletedObjects) + .map(this::getUpdated) // If the object has been removed, tx.get will return null, otherwise it will return me.getValue() + .filter(Objects::nonNull); + + // In case of created values stored in MapKeycloakTransaction, we need filter those according to the filter + MapModelCriteriaBuilder mapMcb = mcb.unwrap(MapModelCriteriaBuilder.class); + Stream res = mapMcb == null + ? updatedAndNotRemovedObjectsStream + : Stream.concat( + createdValuesStream(mapMcb.getKeyFilter(), mapMcb.getEntityFilter()), + updatedAndNotRemovedObjectsStream + ); + + return res; + } + + @Override + public long getCount(ModelCriteriaBuilder mcb) { + return read(mcb).count(); + } + + @Override + public V getUpdated(V orig) { + MapTaskWithValue current = orig == null ? null : tasks.get(orig.getId()); + return current == null ? orig : current.getValue(); + } + + @Override + public void update(K key, V value) { + addTask(key, new UpdateOperation(key, value)); + } + + @Override + public void create(K key, V value) { + addTask(key, new CreateOperation(key, value)); + } + + @Override + public void updateIfChanged(K key, V value, Predicate shouldPut) { + log.tracef("Adding operation UPDATE_IF_CHANGED for %s @ %08x", key, System.identityHashCode(value)); + + K taskKey = key; + MapTaskWithValue op = new MapTaskWithValue(value) { + @Override + public void execute() { + if (shouldPut.test(getValue())) { + map.update(key, getValue()); + } + } + @Override public MapOperation getOperation() { return MapOperation.UPDATE; } + }; + tasks.merge(taskKey, op, this::merge); + } + + @Override + public void delete(K key) { + addTask(key, new DeleteOperation(key)); + } + + + @Override + public long delete(K artificialKey, ModelCriteriaBuilder mcb) { + log.tracef("Adding operation DELETE_BULK"); + + // Remove all tasks that create / update / delete objects deleted by the bulk removal. + final BulkDeleteOperation bdo = new BulkDeleteOperation(mcb); + Predicate filterForNonDeletedObjects = bdo.getFilterForNonDeletedObjects(); + long res = 0; + for (Iterator> it = tasks.entrySet().iterator(); it.hasNext();) { + Entry me = it.next(); + if (! filterForNonDeletedObjects.test(me.getValue().getValue())) { + log.tracef(" [DELETE_BULK] removing %s", me.getKey()); + it.remove(); + res++; + } + } + + tasks.put(artificialKey, bdo); + + return res + bdo.getCount(); + } + + private Stream createdValuesStream(Predicate keyFilter, Predicate entityFilter) { + return this.tasks.entrySet().stream() + .filter(me -> keyFilter.test(me.getKey())) + .map(Map.Entry::getValue) + .filter(v -> v.containsCreate() && ! v.isReplace()) + .map(MapTaskWithValue::getValue) + .filter(Objects::nonNull) + .filter(entityFilter) + // make a snapshot + .collect(Collectors.toList()).stream(); + } + + private MapTaskWithValue merge(MapTaskWithValue oldValue, MapTaskWithValue newValue) { + switch (newValue.getOperation()) { + case DELETE: + return oldValue.containsCreate() ? null : newValue; + default: + return new MapTaskCompose(oldValue, newValue); + } + } + + protected abstract class MapTaskWithValue { + protected final V value; + + public MapTaskWithValue(V value) { + this.value = value; + } + + public V getValue() { + return value; + } + + public boolean containsCreate() { + return MapOperation.CREATE == getOperation(); + } + + public boolean containsRemove() { + return MapOperation.DELETE == getOperation(); + } + + public boolean isReplace() { + return false; + } + + public abstract MapOperation getOperation(); + public abstract void execute(); + } + + private class MapTaskCompose extends MapTaskWithValue { + + private final MapTaskWithValue oldValue; + private final MapTaskWithValue newValue; + + public MapTaskCompose(MapTaskWithValue oldValue, MapTaskWithValue newValue) { + super(null); + this.oldValue = oldValue; + this.newValue = newValue; + } + + @Override + public void execute() { + oldValue.execute(); + newValue.execute(); + } + + @Override + public V getValue() { + return newValue.getValue(); + } + + @Override + public MapOperation getOperation() { + return null; + } + + @Override + public boolean containsCreate() { + return oldValue.containsCreate() || newValue.containsCreate(); + } + + @Override + public boolean containsRemove() { + return oldValue.containsRemove() || newValue.containsRemove(); + } + + @Override + public boolean isReplace() { + return (newValue.getOperation() == MapOperation.CREATE && oldValue.containsRemove()) || + (oldValue instanceof ConcurrentHashMapKeycloakTransaction.MapTaskCompose && ((MapTaskCompose) oldValue).isReplace()); + } + } + + private class CreateOperation extends MapTaskWithValue { + private final K key; + + public CreateOperation(K key, V value) { + super(value); + this.key = key; + } + + @Override public void execute() { map.create(key, getValue()); } + @Override public MapOperation getOperation() { return MapOperation.CREATE; } + } + + private class UpdateOperation extends MapTaskWithValue { + private final K key; + + public UpdateOperation(K key, V value) { + super(value); + this.key = key; + } + + @Override public void execute() { map.update(key, getValue()); } + @Override public MapOperation getOperation() { return MapOperation.UPDATE; } + } + + private class DeleteOperation extends MapTaskWithValue { + private final K key; + + public DeleteOperation(K key) { + super(null); + this.key = key; + } + + @Override public void execute() { map.delete(key); } + @Override public MapOperation getOperation() { return MapOperation.DELETE; } + } + + private class BulkDeleteOperation extends MapTaskWithValue { + + private final ModelCriteriaBuilder mcb; + + public BulkDeleteOperation(ModelCriteriaBuilder mcb) { + super(null); + this.mcb = mcb; + } + + @Override + @SuppressWarnings("unchecked") + public void execute() { + map.delete(mcb); + } + + public Predicate getFilterForNonDeletedObjects() { + if (! (mcb instanceof MapModelCriteriaBuilder)) { + return t -> true; + } + + @SuppressWarnings("unchecked") + final MapModelCriteriaBuilder mmcb = (MapModelCriteriaBuilder) mcb; + + Predicate entityFilter = mmcb.getEntityFilter(); + Predicate keyFilter = ((MapModelCriteriaBuilder) mcb).getKeyFilter(); + return v -> v == null || ! (keyFilter.test(v.getId()) && entityFilter.test(v)); + } + + @Override + public MapOperation getOperation() { + return MapOperation.DELETE; + } + + private long getCount() { + return map.getCount(mcb); + } + } +} \ No newline at end of file diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java index 4603f4071f6..1fc327f4003 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorage.java @@ -17,10 +17,10 @@ package org.keycloak.models.map.storage.chm; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapModelCriteriaBuilder; import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.storage.MapFieldPredicates; -import org.keycloak.models.map.storage.MapKeycloakTransaction; import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.ModelCriteriaBuilder; import org.keycloak.storage.SearchableModelField; @@ -107,8 +107,8 @@ public class ConcurrentHashMapStorage, M> impleme @Override @SuppressWarnings("unchecked") public MapKeycloakTransaction createTransaction(KeycloakSession session) { - MapKeycloakTransaction sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class); - return sessionTransaction == null ? new MapKeycloakTransaction<>(this) : (MapKeycloakTransaction) sessionTransaction; + MapKeycloakTransaction sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class); + return sessionTransaction == null ? new ConcurrentHashMapKeycloakTransaction<>(this) : sessionTransaction; } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java index f3811d86c01..952f3716af7 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/UserSessionConcurrentHashMapStorage.java @@ -39,7 +39,7 @@ public class UserSessionConcurrentHashMapStorage extends ConcurrentHashMapSto private final ConcurrentHashMapStorage, AuthenticatedClientSessionModel> clientSessionStore; - private class Transaction extends MapKeycloakTransaction, UserSessionModel> { + private class Transaction extends ConcurrentHashMapKeycloakTransaction, UserSessionModel> { private final MapKeycloakTransaction, AuthenticatedClientSessionModel> clientSessionTr; @@ -50,7 +50,7 @@ public class UserSessionConcurrentHashMapStorage extends ConcurrentHashMapSto @Override public long delete(K artificialKey, ModelCriteriaBuilder mcb) { - Set ids = getUpdatedNotRemoved(mcb).map(AbstractEntity::getId).collect(Collectors.toSet()); + Set ids = read(mcb).map(AbstractEntity::getId).collect(Collectors.toSet()); ModelCriteriaBuilder csMcb = clientSessionStore.createCriteriaBuilder().compare(AuthenticatedClientSessionModel.SearchableFields.USER_SESSION_ID, Operator.IN, ids); clientSessionTr.delete(artificialKey, csMcb); return super.delete(artificialKey, mcb); @@ -75,7 +75,7 @@ public class UserSessionConcurrentHashMapStorage extends ConcurrentHashMapSto @Override @SuppressWarnings("unchecked") public MapKeycloakTransaction, UserSessionModel> createTransaction(KeycloakSession session) { - MapKeycloakTransaction sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class); - return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : (MapKeycloakTransaction, UserSessionModel>) sessionTransaction; + MapKeycloakTransaction, UserSessionModel> sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class); + return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : sessionTransaction; } } diff --git a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java index 3c653dd93f9..1ec4af7679e 100644 --- a/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/user/MapUserProvider.java @@ -173,7 +173,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.IDP_AND_USER, Operator.EQ, socialProvider); - tx.getUpdatedNotRemoved(mcb) + tx.read(mcb) .map(e -> registerEntityForChanges(tx, e)) .forEach(userEntity -> userEntity.removeFederatedIdentity(socialProvider)); } @@ -209,7 +209,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.IDP_AND_USER, Operator.EQ, socialLink.getIdentityProvider(), socialLink.getUserId()); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .collect(Collectors.collectingAndThen( Collectors.toList(), list -> { @@ -298,7 +298,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, client.getRealm().getId()) .compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.EQ, client.getId()); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .collect(Collectors.collectingAndThen( Collectors.toList(), list -> { @@ -382,7 +382,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { s.map(e -> registerEntityForChanges(tx, e)) .forEach(userEntity -> userEntity.setFederationLink(null)); } @@ -396,7 +396,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, roleId); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { s.map(e -> registerEntityForChanges(tx, e)) .forEach(userEntity -> userEntity.removeRolesMembership(roleId)); } @@ -410,7 +410,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, groupId); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { s.map(e -> registerEntityForChanges(tx, e)) .forEach(userEntity -> userEntity.removeGroupsMembership(groupId)); } @@ -424,7 +424,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.CONSENT_FOR_CLIENT, Operator.EQ, clientId); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { s.map(e -> registerEntityForChanges(tx, e)) .forEach(userEntity -> userEntity.removeUserConsent(clientId)); } @@ -444,7 +444,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, clientScope.getRealm().getId()) .compare(SearchableFields.CONSENT_WITH_CLIENT_SCOPE, Operator.EQ, clientScopeId); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { s.flatMap(MapUserEntity::getUserConsents) .forEach(consent -> consent.removeGrantedClientScopesIds(clientScopeId)); } @@ -462,7 +462,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, Operator.EQ, componentId); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { String providerIdS = new StorageId(componentId, "").getId(); s.forEach(removeConsentsForExternalClient(providerIdS)); } @@ -490,7 +490,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS ModelCriteriaBuilder mcb = userStore.createCriteriaBuilder() .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { s.map(e -> registerEntityForChanges(tx, e)) .forEach(entity -> entity.addRolesMembership(roleId)); } @@ -510,7 +510,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.USERNAME, Operator.ILIKE, username); - try (Stream> s = tx.getUpdatedNotRemoved(mcb)) { + try (Stream> s = tx.read(mcb)) { return s.findFirst() .map(entityToAdapterFunc(realm)).orElse(null); } @@ -523,7 +523,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.EMAIL, Operator.EQ, email); - List> usersWithEmail = tx.getUpdatedNotRemoved(mcb) + List> usersWithEmail = tx.read(mcb) .filter(userEntity -> Objects.equals(userEntity.getEmail(), email)) .collect(Collectors.toList()); if (usersWithEmail.isEmpty()) return null; @@ -584,7 +584,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS mcb = mcb.compare(SearchableFields.SERVICE_ACCOUNT_CLIENT, Operator.NOT_EXISTS); } - return paginatedStream(tx.getUpdatedNotRemoved(mcb) + return paginatedStream(tx.read(mcb) .sorted(MapUserEntity.COMPARE_BY_USERNAME), firstResult, maxResults) .map(entityToAdapterFunc(realm)); } @@ -701,7 +701,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS mcb = mcb.compare(SearchableFields.ASSIGNED_GROUP, Operator.IN, authorizedGroups); } - Stream> usersStream = tx.getUpdatedNotRemoved(mcb) + Stream> usersStream = tx.read(mcb) .sorted(MapUserEntity.COMPARE_BY_USERNAME); // Sort before paginating return paginatedStream(usersStream, firstResult, maxResults) // paginate if necessary @@ -716,7 +716,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, group.getId()); - return paginatedStream(tx.getUpdatedNotRemoved(mcb).sorted(MapUserEntity.COMPARE_BY_USERNAME), firstResult, maxResults) + return paginatedStream(tx.read(mcb).sorted(MapUserEntity.COMPARE_BY_USERNAME), firstResult, maxResults) .map(entityToAdapterFunc(realm)); } @@ -727,7 +727,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ATTRIBUTE, Operator.EQ, attrName, attrValue); - return tx.getUpdatedNotRemoved(mcb) + return tx.read(mcb) .sorted(MapUserEntity.COMPARE_BY_USERNAME) .map(entityToAdapterFunc(realm)); } @@ -756,7 +756,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialS .compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) .compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId()); - return paginatedStream(tx.getUpdatedNotRemoved(mcb) + return paginatedStream(tx.read(mcb) .sorted(MapUserEntity.COMPARE_BY_USERNAME), firstResult, maxResults) .map(entityToAdapterFunc(realm)); } diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java index 556867cbd6f..d5edc071659 100644 --- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java @@ -195,7 +195,7 @@ public class MapUserSessionProvider implements UserSessionProvider { .compare(AuthenticatedClientSessionModel.SearchableFields.CLIENT_ID, ModelCriteriaBuilder.Operator.EQ, client.getId()) .compare(AuthenticatedClientSessionModel.SearchableFields.IS_OFFLINE, ModelCriteriaBuilder.Operator.EQ, offline); - return clientSessionTx.getUpdatedNotRemoved(mcb) + return clientSessionTx.read(mcb) .findFirst() .map(clientEntityToAdapterFunc(client.getRealm(), client, userSession)) .orElse(null); @@ -258,7 +258,7 @@ public class MapUserSessionProvider implements UserSessionProvider { ModelCriteriaBuilder mcb = realmAndOfflineCriteriaBuilder(realm, false) .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uuid); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .findFirst() .map(userEntityToAdapterFunc(realm)) .orElse(null); @@ -271,7 +271,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getUserSessionsStream(%s, %s)%s", realm, user, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .map(userEntityToAdapterFunc(realm)) .filter(Objects::nonNull); } @@ -283,7 +283,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getUserSessionsStream(%s, %s)%s", realm, client, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .map(userEntityToAdapterFunc(realm)) .filter(Objects::nonNull); } @@ -302,7 +302,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getUserSessionByBrokerUserIdStream(%s, %s)%s", realm, brokerUserId, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .map(userEntityToAdapterFunc(realm)) .filter(Objects::nonNull); } @@ -314,7 +314,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getUserSessionByBrokerSessionId(%s, %s)%s", realm, brokerSessionId, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .findFirst() .map(userEntityToAdapterFunc(realm)) .orElse(null); @@ -356,7 +356,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getActiveClientSessionStats(%s, %s)%s", realm, offline, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .map(userEntityToAdapterFunc(realm)) .filter(Objects::nonNull) .map(UserSessionModel::getAuthenticatedClientSessions) @@ -496,7 +496,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getOfflineUserSessionsStream(%s, %s)%s", realm, user, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .map(userEntityToAdapterFunc(realm)) .filter(Objects::nonNull); } @@ -508,7 +508,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getOfflineUserSessionByBrokerSessionId(%s, %s)%s", realm, brokerSessionId, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .findFirst() .map(userEntityToAdapterFunc(realm)) .orElse(null); @@ -521,7 +521,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getOfflineUserSessionByBrokerUserIdStream(%s, %s)%s", realm, brokerUserId, getShortStackTrace()); - return userSessionTx.getUpdatedNotRemoved(mcb) + return userSessionTx.read(mcb) .map(userEntityToAdapterFunc(realm)) .filter(Objects::nonNull); } @@ -544,7 +544,7 @@ public class MapUserSessionProvider implements UserSessionProvider { LOG.tracef("getOfflineUserSessionsStream(%s, %s, %s, %s)%s", realm, client, firstResult, maxResults, getShortStackTrace()); - return paginatedStream(userSessionTx.getUpdatedNotRemoved(mcb) + return paginatedStream(userSessionTx.read(mcb) .map(userEntityToAdapterFunc(realm)) .filter(Objects::nonNull) .sorted(Comparator.comparing(UserSessionModel::getLastSessionRefresh)), firstResult, maxResults); @@ -595,7 +595,7 @@ public class MapUserSessionProvider implements UserSessionProvider { .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uuid); // check if it's an offline user session - MapUserSessionEntity userSessionEntity = userSessionTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null); + MapUserSessionEntity userSessionEntity = userSessionTx.read(mcb).findFirst().orElse(null); if (userSessionEntity != null) { if (userSessionEntity.isOffline()) { return Stream.of(userSessionEntity); @@ -604,7 +604,7 @@ public class MapUserSessionProvider implements UserSessionProvider { // no session found by the given ID, try to find by corresponding session ID mcb = realmAndOfflineCriteriaBuilder(realm, true) .compare(UserSessionModel.SearchableFields.CORRESPONDING_SESSION_ID, ModelCriteriaBuilder.Operator.EQ, userSessionId); - return userSessionTx.getUpdatedNotRemoved(mcb); + return userSessionTx.read(mcb); } // it's online user session so lookup offline user session by corresponding session id reference @@ -613,7 +613,7 @@ public class MapUserSessionProvider implements UserSessionProvider { UK uk = userSessionStore.getKeyConvertor().fromStringSafe(offlineUserSessionId); mcb = realmAndOfflineCriteriaBuilder(realm, true) .compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uk); - return userSessionTx.getUpdatedNotRemoved(mcb); + return userSessionTx.read(mcb); } return Stream.empty();