KEYCLOAK-18369 Create MapKeycloakTransaction interface

This commit is contained in:
Michal Hajas 2021-06-08 15:22:19 +02:00 committed by Hynek Mlnařík
parent 7ffa2835ef
commit d2a8a95d79
16 changed files with 574 additions and 458 deletions

View file

@ -142,7 +142,7 @@ public class MapPermissionTicketStore<K extends Comparable<K>> 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<K extends Comparable<K>> implements Permis
public List<PermissionTicket> 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<K extends Comparable<K>> implements Permis
public List<PermissionTicket> 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<K extends Comparable<K>> implements Permis
public List<PermissionTicket> 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<K extends Comparable<K>> implements Permis
public List<PermissionTicket> 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<K extends Comparable<K>> implements Permis
);
Comparator<? super MapPermissionTicketEntity<K>> 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<K extends Comparable<K>> 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<K extends Comparable<K>> implements Permis
ModelCriteriaBuilder<PermissionTicket> 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()

View file

@ -111,7 +111,7 @@ public class MapPolicyStore<K> 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<K> 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<K> implements PolicyStore {
public List<Policy> 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<K> 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<K>::getId)
.map(K::toString)
@ -205,7 +205,7 @@ public class MapPolicyStore<K> implements PolicyStore {
public void findByResource(String resourceId, String resourceServerId, Consumer<Policy> 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<K> implements PolicyStore {
@Override
public void findByResourceType(String type, String resourceServerId, Consumer<Policy> 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<K> implements PolicyStore {
@Override
public List<Policy> findByScopeIds(List<String> 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<K> 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<Policy> 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<K> implements PolicyStore {
@Override
public List<Policy> 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());

View file

@ -113,7 +113,7 @@ public class MapResourceStore<K extends Comparable<K>> 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<K extends Comparable<K>> implements ResourceStore
private void findByOwnerFilter(String ownerId, String resourceServerId, Consumer<Resource> consumer, int firstResult, int maxResult) {
LOG.tracef("findByOwnerFilter(%s, %s, %s, %d, %d)%s", ownerId, resourceServerId, consumer, firstResult, maxResult, getShortStackTrace());
Comparator<? super MapResourceEntity<K>> 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<K extends Comparable<K>> implements ResourceStore
public List<Resource> 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<K extends Comparable<K>> implements ResourceStore
public List<Resource> 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<K extends Comparable<K>> 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<K extends Comparable<K>> implements ResourceStore
public void findByScope(List<String> scopes, String resourceServerId, Consumer<Resource> 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<K extends Comparable<K>> 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<K extends Comparable<K>> implements ResourceStore
@Override
public void findByType(String type, String resourceServerId, Consumer<Resource> 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<K extends Comparable<K>> 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<K extends Comparable<K>> implements ResourceStore
@Override
public void findByTypeInstance(String type, String resourceServerId, Consumer<Resource> 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)

View file

@ -109,7 +109,7 @@ public class MapScopeStore<K> 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<K> 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<K> implements ScopeStore {
public List<Scope> 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<K> 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());
}
}

View file

@ -134,7 +134,7 @@ public class MapClientProvider<K> implements ClientProvider {
ModelCriteriaBuilder<ClientModel> 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<K> 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<K> implements ClientProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.CLIENT_ID, Operator.ILIKE, "%" + clientId + "%");
Stream<MapClientEntity<K>> s = tx.getUpdatedNotRemoved(mcb)
Stream<MapClientEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_CLIENT_ID);
return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm));
@ -280,7 +280,7 @@ public class MapClientProvider<K> implements ClientProvider {
mcb = mcb.compare(SearchableFields.ATTRIBUTE, Operator.EQ, entry.getKey(), entry.getValue());
}
Stream<MapClientEntity<K>> s = tx.getUpdatedNotRemoved(mcb)
Stream<MapClientEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_CLIENT_ID);
return paginatedStream(s, firstResult, maxResults).map(entityToAdapterFunc(realm));
@ -344,7 +344,7 @@ public class MapClientProvider<K> implements ClientProvider {
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ENABLED, Operator.EQ, Boolean.TRUE);
try (Stream<MapClientEntity<K>> st = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapClientEntity<K>> st = tx.read(mcb)) {
return st
.filter(mce -> mce.getRedirectUris() != null && ! mce.getRedirectUris().isEmpty())
.collect(Collectors.toMap(
@ -358,7 +358,7 @@ public class MapClientProvider<K> implements ClientProvider {
ModelCriteriaBuilder<ClientModel> mcb = clientStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.SCOPE_MAPPING_ROLE, Operator.EQ, role.getId());
try (Stream<MapClientEntity<K>> toRemove = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapClientEntity<K>> toRemove = tx.read(mcb)) {
toRemove
.map(clientEntity -> session.clients().getClientById(realm, clientEntity.getId().toString()))
.filter(Objects::nonNull)

View file

@ -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<K> implements ClientScopeProvider {
ModelCriteriaBuilder<ClientScopeModel> 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));
}

View file

@ -100,7 +100,7 @@ public class MapGroupProvider<K> 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<K> implements GroupProvider {
mcb = mcb.compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%");
}
Stream<GroupModel> groupModelStream = tx.getUpdatedNotRemoved(mcb)
Stream<GroupModel> groupModelStream = tx.read(mcb)
.map(entityToAdapterFunc(realm))
.sorted(Comparator.comparing(GroupModel::getName));
@ -261,7 +261,7 @@ public class MapGroupProvider<K> implements GroupProvider {
.compare(SearchableFields.PARENT_ID, Operator.EQ, parentId)
.compare(SearchableFields.NAME, Operator.EQ, group.getName());
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapGroupEntity<K>> 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<K> implements GroupProvider {
.compare(SearchableFields.PARENT_ID, Operator.EQ, (Object) null)
.compare(SearchableFields.NAME, Operator.EQ, subGroup.getName());
try (Stream<MapGroupEntity<K>> possibleSiblings = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapGroupEntity<K>> 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<K> implements GroupProvider {
ModelCriteriaBuilder<GroupModel> mcb = groupStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, role.getId());
try (Stream<MapGroupEntity<K>> toRemove = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapGroupEntity<K>> toRemove = tx.read(mcb)) {
toRemove
.map(groupEntity -> session.groups().getGroupById(realm, groupEntity.getId().toString()))
.forEach(groupModel -> groupModel.deleteRoleMapping(role));

View file

@ -66,7 +66,7 @@ public class MapUserLoginFailureProvider<K> 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<K> implements UserLoginFailureProvider
LOG.tracef("addUserLoginFailure(%s, %s)%s", realm, userId, getShortStackTrace());
MapUserLoginFailureEntity<K> userLoginFailureEntity = userLoginFailureTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
MapUserLoginFailureEntity<K> userLoginFailureEntity = userLoginFailureTx.read(mcb).findFirst().orElse(null);
if (userLoginFailureEntity == null) {
userLoginFailureEntity = new MapUserLoginFailureEntity<>(userLoginFailureStore.getKeyConvertor().yieldNewUniqueKey(), realm.getId(), userId);

View file

@ -110,7 +110,7 @@ public class MapRealmProvider<K> implements RealmProvider {
ModelCriteriaBuilder<RealmModel> mcb = realmStore.createCriteriaBuilder()
.compare(SearchableFields.NAME, Operator.EQ, name);
K realmId = tx.getUpdatedNotRemoved(mcb)
K realmId = tx.read(mcb)
.findFirst()
.map(MapRealmEntity<K>::getId)
.orElse(null);
@ -132,7 +132,7 @@ public class MapRealmProvider<K> implements RealmProvider {
}
private Stream<RealmModel> getRealmsStream(ModelCriteriaBuilder<RealmModel> mcb) {
return tx.getUpdatedNotRemoved(mcb)
return tx.read(mcb)
.map(this::entityToAdapter)
.sorted(RealmModel.COMPARE_BY_NAME);
}
@ -174,7 +174,7 @@ public class MapRealmProvider<K> implements RealmProvider {
ModelCriteriaBuilder<RealmModel> mcb = realmStore.createCriteriaBuilder()
.compare(SearchableFields.CLIENT_INITIAL_ACCESS, Operator.EXISTS);
tx.getUpdatedNotRemoved(mcb)
tx.read(mcb)
.map(e -> registerEntityForChanges(tx, e))
.forEach(MapRealmEntity<K>::removeExpiredClientInitialAccesses);
}

View file

@ -110,7 +110,7 @@ public class MapRoleProvider<K> 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<K> 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<K> implements RoleProvider {
.compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false);
//remove role from realm-roles composites
try (Stream<MapRoleEntity<K>> baseStream = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapRoleEntity<K>> baseStream = tx.read(mcb)) {
StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity<K>::getCompositeRoles)
.filter(pair -> role.getId().equals(pair.getV()))
@ -189,7 +189,7 @@ public class MapRoleProvider<K> implements RoleProvider {
.compare(SearchableFields.CLIENT_ID, Operator.EQ, client.getId())
.compare(SearchableFields.IS_COMPOSITE_ROLE, Operator.EQ, false);
try (Stream<MapRoleEntity<K>> baseStream = tx.getUpdatedNotRemoved(mcbClient)) {
try (Stream<MapRoleEntity<K>> baseStream = tx.read(mcbClient)) {
StreamUtils.leftInnerJoinIterable(baseStream, MapRoleEntity<K>::getCompositeRoles)
.filter(pair -> role.getId().equals(pair.getV()))
@ -246,7 +246,7 @@ public class MapRoleProvider<K> 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<K> 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<K> implements RoleProvider {
roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%")
);
Stream<MapRoleEntity<K>> s = tx.getUpdatedNotRemoved(mcb)
Stream<MapRoleEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_NAME);
return paginatedStream(s.map(entityToAdapterFunc(realm)), first, max);
@ -321,7 +321,7 @@ public class MapRoleProvider<K> implements RoleProvider {
roleStore.createCriteriaBuilder().compare(SearchableFields.NAME, Operator.ILIKE, "%" + search + "%"),
roleStore.createCriteriaBuilder().compare(SearchableFields.DESCRIPTION, Operator.ILIKE, "%" + search + "%")
);
Stream<MapRoleEntity<K>> s = tx.getUpdatedNotRemoved(mcb)
Stream<MapRoleEntity<K>> s = tx.read(mcb)
.sorted(COMPARE_BY_NAME);
return paginatedStream(s,first, max).map(entityToAdapterFunc(client.getRealm()));

View file

@ -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<K, V extends AbstractEntity<K>, 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<K, MapTaskWithValue> tasks = new LinkedHashMap<>();
private final MapStorage<K, V, M> map;
public MapKeycloakTransaction(MapStorage<K, V, M> 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<K, V extends AbstractEntity<K>, 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<K, V> 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<V> getUpdatedNotRemoved(ModelCriteriaBuilder<M> mcb) {
Predicate<? super V> filterOutAllBulkDeletedObjects = tasks.values().stream()
.filter(BulkDeleteOperation.class::isInstance)
.map(BulkDeleteOperation.class::cast)
.map(BulkDeleteOperation::getFilterForNonDeletedObjects)
.reduce(Predicate::and)
.orElse(v -> true);
Stream<V> 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<K, V, M> mapMcb = mcb.unwrap(MapModelCriteriaBuilder.class);
Stream<V> 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<M> 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<K, V> 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<V> read(ModelCriteriaBuilder<M> 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<M> 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<V> 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.
* 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 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
* @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<M> mcb) {
log.tracef("Adding operation DELETE_BULK");
void updateIfChanged(K key, V value, Predicate<V> shouldPut);
/**
* 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);
/**
* 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<M> mcb);
// Remove all tasks that create / update / delete objects deleted by the bulk removal.
final BulkDeleteOperation bdo = new BulkDeleteOperation(mcb);
Predicate<V> filterForNonDeletedObjects = bdo.getFilterForNonDeletedObjects();
long res = 0;
for (Iterator<Entry<K, MapTaskWithValue>> it = tasks.entrySet().iterator(); it.hasNext();) {
Entry<K, MapTaskWithValue> 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<V> createdValuesStream(Predicate<? super K> keyFilter, Predicate<? super V> 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<M> mcb;
public BulkDeleteOperation(ModelCriteriaBuilder<M> mcb) {
super(null);
this.mcb = mcb;
}
@Override
@SuppressWarnings("unchecked")
public void execute() {
map.delete(mcb);
}
public Predicate<V> getFilterForNonDeletedObjects() {
if (! (mcb instanceof MapModelCriteriaBuilder)) {
return t -> true;
}
@SuppressWarnings("unchecked")
final MapModelCriteriaBuilder<K, V, M> mmcb = (MapModelCriteriaBuilder<K, V, M>) mcb;
Predicate<? super V> entityFilter = mmcb.getEntityFilter();
Predicate<? super K> 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);
}
}
}

View file

@ -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<K, V extends AbstractEntity<K>, M> implements MapKeycloakTransaction<K, V, M> {
private final static Logger log = Logger.getLogger(ConcurrentHashMapKeycloakTransaction.class);
private boolean active;
private boolean rollback;
private final Map<K, MapTaskWithValue> tasks = new LinkedHashMap<>();
private final MapStorage<K, V, M> map;
enum MapOperation {
CREATE, UPDATE, DELETE,
}
public ConcurrentHashMapKeycloakTransaction(MapStorage<K, V, M> 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<K, V> 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<V> read(ModelCriteriaBuilder<M> mcb) {
Predicate<? super V> filterOutAllBulkDeletedObjects = tasks.values().stream()
.filter(BulkDeleteOperation.class::isInstance)
.map(BulkDeleteOperation.class::cast)
.map(BulkDeleteOperation::getFilterForNonDeletedObjects)
.reduce(Predicate::and)
.orElse(v -> true);
Stream<V> 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<K, V, M> mapMcb = mcb.unwrap(MapModelCriteriaBuilder.class);
Stream<V> res = mapMcb == null
? updatedAndNotRemovedObjectsStream
: Stream.concat(
createdValuesStream(mapMcb.getKeyFilter(), mapMcb.getEntityFilter()),
updatedAndNotRemovedObjectsStream
);
return res;
}
@Override
public long getCount(ModelCriteriaBuilder<M> 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<V> 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<M> 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<V> filterForNonDeletedObjects = bdo.getFilterForNonDeletedObjects();
long res = 0;
for (Iterator<Entry<K, MapTaskWithValue>> it = tasks.entrySet().iterator(); it.hasNext();) {
Entry<K, MapTaskWithValue> 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<V> createdValuesStream(Predicate<? super K> keyFilter, Predicate<? super V> 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<M> mcb;
public BulkDeleteOperation(ModelCriteriaBuilder<M> mcb) {
super(null);
this.mcb = mcb;
}
@Override
@SuppressWarnings("unchecked")
public void execute() {
map.delete(mcb);
}
public Predicate<V> getFilterForNonDeletedObjects() {
if (! (mcb instanceof MapModelCriteriaBuilder)) {
return t -> true;
}
@SuppressWarnings("unchecked")
final MapModelCriteriaBuilder<K, V, M> mmcb = (MapModelCriteriaBuilder<K, V, M>) mcb;
Predicate<? super V> entityFilter = mmcb.getEntityFilter();
Predicate<? super K> 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);
}
}
}

View file

@ -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<K, V extends AbstractEntity<K>, M> impleme
@Override
@SuppressWarnings("unchecked")
public MapKeycloakTransaction<K, V, M> createTransaction(KeycloakSession session) {
MapKeycloakTransaction sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class);
return sessionTransaction == null ? new MapKeycloakTransaction<>(this) : (MapKeycloakTransaction<K, V, M>) sessionTransaction;
MapKeycloakTransaction<K, V, M> sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class);
return sessionTransaction == null ? new ConcurrentHashMapKeycloakTransaction<>(this) : sessionTransaction;
}
@Override

View file

@ -39,7 +39,7 @@ public class UserSessionConcurrentHashMapStorage<K> extends ConcurrentHashMapSto
private final ConcurrentHashMapStorage<K, MapAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionStore;
private class Transaction extends MapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel> {
private class Transaction extends ConcurrentHashMapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel> {
private final MapKeycloakTransaction<K, MapAuthenticatedClientSessionEntity<K>, AuthenticatedClientSessionModel> clientSessionTr;
@ -50,7 +50,7 @@ public class UserSessionConcurrentHashMapStorage<K> extends ConcurrentHashMapSto
@Override
public long delete(K artificialKey, ModelCriteriaBuilder<UserSessionModel> mcb) {
Set<K> ids = getUpdatedNotRemoved(mcb).map(AbstractEntity::getId).collect(Collectors.toSet());
Set<K> ids = read(mcb).map(AbstractEntity::getId).collect(Collectors.toSet());
ModelCriteriaBuilder<AuthenticatedClientSessionModel> 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<K> extends ConcurrentHashMapSto
@Override
@SuppressWarnings("unchecked")
public MapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel> createTransaction(KeycloakSession session) {
MapKeycloakTransaction sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class);
return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : (MapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel>) sessionTransaction;
MapKeycloakTransaction<K, MapUserSessionEntity<K>, UserSessionModel> sessionTransaction = session.getAttribute("map-transaction-" + hashCode(), MapKeycloakTransaction.class);
return sessionTransaction == null ? new Transaction(clientSessionStore.createTransaction(session)) : sessionTransaction;
}
}

View file

@ -173,7 +173,7 @@ public class MapUserProvider<K> 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<K> 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<K> 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<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.FEDERATION_LINK, Operator.EQ, storageProviderId);
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.setFederationLink(null));
}
@ -396,7 +396,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_ROLE, Operator.EQ, roleId);
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeRolesMembership(roleId));
}
@ -410,7 +410,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.ASSIGNED_GROUP, Operator.EQ, groupId);
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeGroupsMembership(groupId));
}
@ -424,7 +424,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.CONSENT_FOR_CLIENT, Operator.EQ, clientId);
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(userEntity -> userEntity.removeUserConsent(clientId));
}
@ -444,7 +444,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, clientScope.getRealm().getId())
.compare(SearchableFields.CONSENT_WITH_CLIENT_SCOPE, Operator.EQ, clientScopeId);
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
s.flatMap(MapUserEntity::getUserConsents)
.forEach(consent -> consent.removeGrantedClientScopesIds(clientScopeId));
}
@ -462,7 +462,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.CONSENT_CLIENT_FEDERATION_LINK, Operator.EQ, componentId);
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
String providerIdS = new StorageId(componentId, "").getId();
s.forEach(removeConsentsForExternalClient(providerIdS));
}
@ -490,7 +490,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
ModelCriteriaBuilder<UserModel> mcb = userStore.createCriteriaBuilder()
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId());
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
s.map(e -> registerEntityForChanges(tx, e))
.forEach(entity -> entity.addRolesMembership(roleId));
}
@ -510,7 +510,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.USERNAME, Operator.ILIKE, username);
try (Stream<MapUserEntity<K>> s = tx.getUpdatedNotRemoved(mcb)) {
try (Stream<MapUserEntity<K>> s = tx.read(mcb)) {
return s.findFirst()
.map(entityToAdapterFunc(realm)).orElse(null);
}
@ -523,7 +523,7 @@ public class MapUserProvider<K> implements UserProvider.Streams, UserCredentialS
.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.EMAIL, Operator.EQ, email);
List<MapUserEntity<K>> usersWithEmail = tx.getUpdatedNotRemoved(mcb)
List<MapUserEntity<K>> 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<K> 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<K> implements UserProvider.Streams, UserCredentialS
mcb = mcb.compare(SearchableFields.ASSIGNED_GROUP, Operator.IN, authorizedGroups);
}
Stream<MapUserEntity<K>> usersStream = tx.getUpdatedNotRemoved(mcb)
Stream<MapUserEntity<K>> 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<K> 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<K> 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<K> 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));
}

View file

@ -195,7 +195,7 @@ public class MapUserSessionProvider<UK, CK> 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<UK, CK> implements UserSessionProvider {
ModelCriteriaBuilder<UserSessionModel> 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<UK, CK> 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<UK, CK> 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<UK, CK> 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<UK, CK> 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<UK, CK> 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<UK, CK> 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<UK, CK> 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<UK, CK> 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<UK, CK> 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<UK, CK> implements UserSessionProvider {
.compare(UserSessionModel.SearchableFields.ID, ModelCriteriaBuilder.Operator.EQ, uuid);
// check if it's an offline user session
MapUserSessionEntity<UK> userSessionEntity = userSessionTx.getUpdatedNotRemoved(mcb).findFirst().orElse(null);
MapUserSessionEntity<UK> userSessionEntity = userSessionTx.read(mcb).findFirst().orElse(null);
if (userSessionEntity != null) {
if (userSessionEntity.isOffline()) {
return Stream.of(userSessionEntity);
@ -604,7 +604,7 @@ public class MapUserSessionProvider<UK, CK> 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<UK, CK> 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();