fix cache
This commit is contained in:
parent
3bd6974ccb
commit
d79a7146ba
35 changed files with 1390 additions and 1997 deletions
|
@ -1,401 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.counter;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.ClientAdapter;
|
||||
import org.keycloak.models.cache.infinispan.ClientTemplateAdapter;
|
||||
import org.keycloak.models.cache.infinispan.GroupAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RealmAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RoleAdapter;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClient;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealmRole;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCacheRealmProvider implements CacheRealmProvider {
|
||||
protected static final Logger logger = Logger.getLogger(RevisionedCacheRealmProvider.class);
|
||||
protected RevisionedRealmCache cache;
|
||||
protected KeycloakSession session;
|
||||
protected RealmProvider delegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
|
||||
protected Set<String> realmInvalidations = new HashSet<>();
|
||||
protected Set<String> appInvalidations = new HashSet<>();
|
||||
protected Set<String> clientTemplateInvalidations = new HashSet<>();
|
||||
protected Set<String> roleInvalidations = new HashSet<>();
|
||||
protected Set<String> groupInvalidations = new HashSet<>();
|
||||
protected Map<String, RealmModel> managedRealms = new HashMap<>();
|
||||
protected Map<String, ClientModel> managedApplications = new HashMap<>();
|
||||
protected Map<String, ClientTemplateModel> managedClientTemplates = new HashMap<>();
|
||||
protected Map<String, RoleModel> managedRoles = new HashMap<>();
|
||||
protected Map<String, GroupModel> managedGroups = new HashMap<>();
|
||||
|
||||
protected boolean clearAll;
|
||||
|
||||
public RevisionedCacheRealmProvider(RevisionedRealmCache cache, KeycloakSession session) {
|
||||
this.cache = cache;
|
||||
this.session = session;
|
||||
|
||||
session.getTransaction().enlistAfterCompletion(getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider getDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(RealmProvider.class);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRealmInvalidation(String id) {
|
||||
realmInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerApplicationInvalidation(String id) {
|
||||
appInvalidations.add(id);
|
||||
}
|
||||
@Override
|
||||
public void registerClientTemplateInvalidation(String id) {
|
||||
clientTemplateInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRoleInvalidation(String id) {
|
||||
roleInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGroupInvalidation(String id) {
|
||||
groupInvalidations.add(id);
|
||||
|
||||
}
|
||||
|
||||
protected void runInvalidations() {
|
||||
for (String id : realmInvalidations) {
|
||||
cache.invalidateCachedRealmById(id);
|
||||
}
|
||||
for (String id : roleInvalidations) {
|
||||
cache.invalidateRoleById(id);
|
||||
}
|
||||
for (String id : groupInvalidations) {
|
||||
cache.invalidateGroupById(id);
|
||||
}
|
||||
for (String id : appInvalidations) {
|
||||
cache.invalidateCachedApplicationById(id);
|
||||
}
|
||||
for (String id : clientTemplateInvalidations) {
|
||||
cache.invalidateCachedClientTemplateById(id);
|
||||
}
|
||||
}
|
||||
|
||||
private KeycloakTransaction getTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
setRollbackOnly = true;
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
RealmModel realm = getDelegate().createRealm(name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmModel realm = getDelegate().createRealm(id, name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
CachedRealm cached = cache.getCachedRealm(id);
|
||||
if (cached != null) {
|
||||
logger.tracev("by id cache hit: {0}", cached.getName());
|
||||
}
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
RealmModel model = getDelegate().getRealm(id);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedRealm(loaded, cache, this, model);
|
||||
logger.tracev("try caching realm: {0} {1}", cached.getName(), loaded);
|
||||
cache.addCachedRealm(cached);
|
||||
} else if (realmInvalidations.contains(id)) {
|
||||
return getDelegate().getRealm(id);
|
||||
} else if (managedRealms.containsKey(id)) {
|
||||
return managedRealms.get(id);
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealmByName(String name) {
|
||||
CachedRealm cached = cache.getCachedRealmByName(name);
|
||||
if (cached != null) {
|
||||
logger.tracev("by name cache hit: {0}", cached.getName());
|
||||
}
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
RealmModel model = getDelegate().getRealmByName(name);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(model.getId())) return model;
|
||||
cached = new RevisionedCachedRealm(loaded, cache, this, model);
|
||||
logger.tracev("try caching realm: {0}", cached.getName());
|
||||
cache.addCachedRealm(cached);
|
||||
} else if (realmInvalidations.contains(cached.getId())) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
} else if (managedRealms.containsKey(cached.getId())) {
|
||||
return managedRealms.get(cached.getId());
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(cached.getId(), adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
// Retrieve realms from backend
|
||||
List<RealmModel> backendRealms = getDelegate().getRealms();
|
||||
|
||||
// Return cache delegates to ensure cache invalidated during write operations
|
||||
List<RealmModel> cachedRealms = new LinkedList<RealmModel>();
|
||||
for (RealmModel realm : backendRealms) {
|
||||
RealmModel cached = getRealm(realm.getId());
|
||||
cachedRealms.add(cached);
|
||||
}
|
||||
return cachedRealms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRealm(String id) {
|
||||
cache.invalidateCachedRealmById(id);
|
||||
|
||||
RealmModel realm = getDelegate().getRealm(id);
|
||||
Set<RoleModel> realmRoles = null;
|
||||
if (realm != null) {
|
||||
realmRoles = realm.getRoles();
|
||||
}
|
||||
|
||||
boolean didIt = getDelegate().removeRealm(id);
|
||||
realmInvalidations.add(id);
|
||||
|
||||
// TODO: Temporary workaround to invalidate cached realm roles
|
||||
if (didIt && realmRoles != null) {
|
||||
for (RoleModel role : realmRoles) {
|
||||
roleInvalidations.add(role.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return didIt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null) delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id, RealmModel realm) {
|
||||
CachedRole cached = cache.getRole(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
RoleModel model = getDelegate().getRoleById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (roleInvalidations.contains(id)) return model;
|
||||
if (model.getContainer() instanceof ClientModel) {
|
||||
cached = new RevisionedCachedClientRole(loaded, ((ClientModel) model.getContainer()).getId(), model, realm);
|
||||
} else {
|
||||
cached = new RevisionedCachedRealmRole(loaded, model, realm);
|
||||
}
|
||||
cache.addCachedRole(cached);
|
||||
|
||||
} else if (roleInvalidations.contains(id)) {
|
||||
return getDelegate().getRoleById(id, realm);
|
||||
} else if (managedRoles.containsKey(id)) {
|
||||
return managedRoles.get(id);
|
||||
}
|
||||
RoleAdapter adapter = new RoleAdapter(cached, cache, this, realm);
|
||||
managedRoles.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||
CachedGroup cached = cache.getGroup(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (groupInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedGroup(loaded, realm, model);
|
||||
cache.addCachedGroup(cached);
|
||||
|
||||
} else if (groupInvalidations.contains(id)) {
|
||||
return getDelegate().getGroupById(id, realm);
|
||||
} else if (managedGroups.containsKey(id)) {
|
||||
return managedGroups.get(id);
|
||||
}
|
||||
GroupAdapter adapter = new GroupAdapter(cached, this, session, realm);
|
||||
managedGroups.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
CachedClient cached = cache.getApplication(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
if (cached != null) {
|
||||
logger.tracev("client by id cache hit: {0}", cached.getClientId());
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (appInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedClient(loaded, cache, getDelegate(), realm, model);
|
||||
cache.addCachedClient(cached);
|
||||
} else if (appInvalidations.contains(id)) {
|
||||
return getDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
return managedApplications.get(id);
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache);
|
||||
managedApplications.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
@Override
|
||||
public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
|
||||
CachedClientTemplate cached = cache.getClientTemplate(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (clientTemplateInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedClientTemplate(loaded, cache, getDelegate(), realm, model);
|
||||
cache.addCachedClientTemplate(cached);
|
||||
} else if (clientTemplateInvalidations.contains(id)) {
|
||||
return getDelegate().getClientTemplateById(id, realm);
|
||||
} else if (managedClientTemplates.containsKey(id)) {
|
||||
return managedClientTemplates.get(id);
|
||||
}
|
||||
ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this, cache);
|
||||
managedClientTemplates.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.counter;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.CacheRealmProviderFactory;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class RevisionedCacheRealmProviderFactory implements CacheRealmProviderFactory {
|
||||
|
||||
private static final Logger log = Logger.getLogger(RevisionedCacheRealmProviderFactory.class);
|
||||
|
||||
protected volatile RevisionedRealmCache realmCache;
|
||||
|
||||
protected final ConcurrentHashMap<String, String> realmLookup = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public CacheRealmProvider create(KeycloakSession session) {
|
||||
lazyInit(session);
|
||||
return new RevisionedCacheRealmProvider(realmCache, session);
|
||||
}
|
||||
|
||||
private void lazyInit(KeycloakSession session) {
|
||||
if (realmCache == null) {
|
||||
synchronized (this) {
|
||||
if (realmCache == null) {
|
||||
Cache<String, Object> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
|
||||
Cache<String, Long> counterCache = session.getProvider(InfinispanConnectionProvider.class).getCache(RevisionedConnectionProviderFactory.VERSION_CACHE_NAME);
|
||||
cache.addListener(new CacheListener());
|
||||
realmCache = new RevisionedRealmCache(cache, counterCache, realmLookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "infinispan-revisioned";
|
||||
}
|
||||
|
||||
@Listener
|
||||
public class CacheListener {
|
||||
|
||||
@CacheEntryCreated
|
||||
public void created(CacheEntryCreatedEvent<String, Object> event) {
|
||||
if (!event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
log.tracev("Realm added realm={0}", realm.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryRemoved
|
||||
public void removed(CacheEntryRemovedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryInvalidated
|
||||
public void removed(CacheEntryInvalidatedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntriesEvicted
|
||||
public void userEvicted(CacheEntriesEvictedEvent<String, Object> event) {
|
||||
for (Object object : event.getEntries().values()) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(Object object) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
|
||||
realmLookup.remove(realm.getName());
|
||||
|
||||
for (String r : realm.getRealmRoles().values()) {
|
||||
realmCache.evictCachedRoleById(r);
|
||||
}
|
||||
|
||||
for (String c : realm.getClients().values()) {
|
||||
realmCache.evictCachedApplicationById(c);
|
||||
}
|
||||
|
||||
log.tracev("Realm removed realm={0}", realm.getName());
|
||||
} else if (object instanceof CachedClient) {
|
||||
CachedClient client = (CachedClient) object;
|
||||
|
||||
for (String r : client.getRoles().values()) {
|
||||
realmCache.evictCachedRoleById(r);
|
||||
}
|
||||
|
||||
log.tracev("Client removed client={0}", client.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.counter;
|
||||
|
||||
import org.infinispan.configuration.cache.Configuration;
|
||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class RevisionedConnectionProviderFactory extends DefaultInfinispanConnectionProviderFactory {
|
||||
public static final String VERSION_CACHE_NAME = "realmVersions";
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(RevisionedConnectionProviderFactory.class);
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "revisioned";
|
||||
}
|
||||
|
||||
|
||||
protected void initEmbedded() {
|
||||
super.initEmbedded();
|
||||
ConfigurationBuilder counterConfigBuilder = new ConfigurationBuilder();
|
||||
Configuration counterCacheConfiguration = counterConfigBuilder.build();
|
||||
|
||||
cacheManager.defineConfiguration(VERSION_CACHE_NAME, counterCacheConfiguration);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.counter;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class RevisionedRealmCache implements RealmCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(RevisionedRealmCache.class);
|
||||
|
||||
protected final Cache<String, Long> revisions;
|
||||
protected final Cache<String, Object> cache;
|
||||
|
||||
protected final ConcurrentHashMap<String, String> realmLookup;
|
||||
|
||||
public RevisionedRealmCache(Cache<String, Object> cache, Cache<String, Long> revisions, ConcurrentHashMap<String, String> realmLookup) {
|
||||
this.cache = cache;
|
||||
this.realmLookup = realmLookup;
|
||||
this.revisions = revisions;
|
||||
}
|
||||
|
||||
public Cache<String, Object> getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
private <T> T get(String id, Class<T> type) {
|
||||
Revisioned o = (Revisioned)cache.get(id);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
Long rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
logger.tracev("get() missing rev");
|
||||
return null;
|
||||
}
|
||||
long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue();
|
||||
if (rev > oRev) {
|
||||
logger.tracev("stale rev: {0} o.rev: {1}", rev.longValue(), oRev);
|
||||
return null;
|
||||
}
|
||||
return o != null && type.isInstance(o) ? type.cast(o) : null;
|
||||
}
|
||||
|
||||
protected Object invalidateObject(String id) {
|
||||
Object removed = cache.remove(id);
|
||||
revisions.put(id, UpdateCounter.next());
|
||||
return removed;
|
||||
}
|
||||
|
||||
protected void addRevisioned(String id, Revisioned object) {
|
||||
Long rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
logger.tracev("rev was null in addRevisioned, adding one");
|
||||
rev = UpdateCounter.next();
|
||||
revisions.put(id, rev);
|
||||
return;
|
||||
}
|
||||
cache.putForExternalRead(id, object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getCachedRealm(String id) {
|
||||
return get(id, CachedRealm.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRealm(CachedRealm realm) {
|
||||
logger.tracev("Invalidating realm {0}", realm.getId());
|
||||
invalidateObject(realm.getId());
|
||||
realmLookup.remove(realm.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRealmById(String id) {
|
||||
CachedRealm cached = (CachedRealm) invalidateObject(id);
|
||||
if (cached != null) realmLookup.remove(cached.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedRealm(CachedRealm realm) {
|
||||
logger.tracev("Adding realm {0}", realm.getId());
|
||||
addRevisioned(realm.getId(), (Revisioned) realm);
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getCachedRealmByName(String name) {
|
||||
String id = realmLookup.get(name);
|
||||
return id != null ? getCachedRealm(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClient getApplication(String id) {
|
||||
return get(id, CachedClient.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateApplication(CachedClient app) {
|
||||
logger.tracev("Removing application {0}", app.getId());
|
||||
invalidateObject(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedClient(CachedClient app) {
|
||||
logger.tracev("Adding application {0}", app.getId());
|
||||
addRevisioned(app.getId(), (Revisioned) app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedApplicationById(String id) {
|
||||
CachedClient client = (CachedClient)invalidateObject(id);
|
||||
if (client != null) logger.tracev("Removing application {0}", client.getClientId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictCachedApplicationById(String id) {
|
||||
logger.tracev("Evicting application {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedGroup getGroup(String id) {
|
||||
return get(id, CachedGroup.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroup(CachedGroup role) {
|
||||
logger.tracev("Removing group {0}", role.getId());
|
||||
invalidateObject(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedGroup(CachedGroup role) {
|
||||
logger.tracev("Adding group {0}", role.getId());
|
||||
addRevisioned(role.getId(), (Revisioned) role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
invalidateObject(id);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRole getRole(String id) {
|
||||
return get(id, CachedRole.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRole(CachedRole role) {
|
||||
logger.tracev("Removing role {0}", role.getId());
|
||||
invalidateObject(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictCachedRoleById(String id) {
|
||||
logger.tracev("Evicting role {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedRole(CachedRole role) {
|
||||
logger.tracev("Adding role {0}", role.getId());
|
||||
addRevisioned(role.getId(), (Revisioned) role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClientTemplate getClientTemplate(String id) {
|
||||
return get(id, CachedClientTemplate.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Removing client template {0}", app.getId());
|
||||
invalidateObject(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Adding client template {0}", app.getId());
|
||||
addRevisioned(app.getId(), (Revisioned) app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedClientTemplateById(String id) {
|
||||
logger.tracev("Removing client template {0}", id);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictCachedClientTemplateById(String id) {
|
||||
logger.tracev("Evicting client template {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -38,12 +38,12 @@ import org.keycloak.models.cache.infinispan.ClientTemplateAdapter;
|
|||
import org.keycloak.models.cache.infinispan.GroupAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RealmAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RoleAdapter;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClient;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.counter.entities.RevisionedCachedRealmRole;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClient;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedRealmRole;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -277,6 +277,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider {
|
|||
}
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
RealmModel model = getDelegate().getRealm(id);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(id)) return model;
|
||||
|
@ -299,10 +300,11 @@ public class LockingCacheRealmProvider implements CacheRealmProvider {
|
|||
logger.tracev("by name cache hit: {0}", cached.getName());
|
||||
}
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
RealmModel model = getDelegate().getRealmByName(name);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(model.getId())) return model;
|
||||
cached = new RevisionedCachedRealm(null, cache, this, model);
|
||||
cached = new RevisionedCachedRealm(loaded, cache, this, model);
|
||||
cache.addCachedRealm(cached);
|
||||
} else if (realmInvalidations.contains(cached.getId())) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
|
@ -365,6 +367,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
RoleModel model = getDelegate().getRoleById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (roleInvalidations.contains(id)) return model;
|
||||
|
@ -394,6 +397,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (groupInvalidations.contains(id)) return model;
|
||||
|
@ -422,6 +426,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (appInvalidations.contains(id)) return model;
|
||||
|
@ -445,6 +450,7 @@ public class LockingCacheRealmProvider implements CacheRealmProvider {
|
|||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (clientTemplateInvalidations.contains(id)) return model;
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.keycloak.models.cache.entities.CachedClientTemplate;
|
|||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
@ -39,11 +38,6 @@ public class LockingRealmCache implements RealmCache {
|
|||
|
||||
protected final Cache<String, Long> revisions;
|
||||
protected final Cache<String, Object> cache;
|
||||
final AtomicLong realmCounter = new AtomicLong();
|
||||
final AtomicLong clientCounter = new AtomicLong();
|
||||
final AtomicLong clientTemplateCounter = new AtomicLong();
|
||||
final AtomicLong roleCounter = new AtomicLong();
|
||||
final AtomicLong groupCounter = new AtomicLong();
|
||||
|
||||
protected final ConcurrentHashMap<String, String> realmLookup;
|
||||
|
||||
|
@ -61,8 +55,8 @@ public class LockingRealmCache implements RealmCache {
|
|||
return revisions;
|
||||
}
|
||||
|
||||
public void startRevisionBatch() {
|
||||
revisions.startBatch();
|
||||
public Long getCurrentRevision(String id) {
|
||||
return revisions.get(id);
|
||||
}
|
||||
|
||||
public void endRevisionBatch() {
|
||||
|
@ -91,33 +85,39 @@ public class LockingRealmCache implements RealmCache {
|
|||
return o != null && type.isInstance(o) ? type.cast(o) : null;
|
||||
}
|
||||
|
||||
protected Object invalidateObject(String id, AtomicLong counter) {
|
||||
protected Object invalidateObject(String id) {
|
||||
Object removed = cache.remove(id);
|
||||
revisions.put(id, counter.incrementAndGet());
|
||||
revisions.put(id, UpdateCounter.next());
|
||||
return removed;
|
||||
}
|
||||
|
||||
protected void addRevisioned(String id, Revisioned object, AtomicLong counter) {
|
||||
protected void addRevisioned(String id, Revisioned object) {
|
||||
//startRevisionBatch();
|
||||
try {
|
||||
//revisions.getAdvancedCache().lock(id);
|
||||
Long rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
rev = counter.incrementAndGet();
|
||||
rev = UpdateCounter.current();
|
||||
revisions.put(id, rev);
|
||||
return;
|
||||
}
|
||||
revisions.startBatch();
|
||||
revisions.getAdvancedCache().lock(id);
|
||||
if (!revisions.getAdvancedCache().lock(id)) {
|
||||
logger.trace("Could not obtain version lock");
|
||||
}
|
||||
rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
rev = counter.incrementAndGet();
|
||||
revisions.put(id, rev);
|
||||
return;
|
||||
}
|
||||
if (rev.equals(object.getRevision())) {
|
||||
cache.putForExternalRead(id, object);
|
||||
return;
|
||||
}
|
||||
if (rev > object.getRevision()) { // revision is ahead, don't cache
|
||||
return;
|
||||
}
|
||||
// revisions cache has a lower value than the object.revision, so update revision and add it to cache
|
||||
revisions.put(id, object.getRevision());
|
||||
cache.putForExternalRead(id, object);
|
||||
} finally {
|
||||
endRevisionBatch();
|
||||
}
|
||||
|
@ -127,9 +127,6 @@ public class LockingRealmCache implements RealmCache {
|
|||
|
||||
|
||||
|
||||
public Long getCurrentRevision(String id) {
|
||||
return revisions.get(id);
|
||||
}
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
|
@ -143,20 +140,20 @@ public class LockingRealmCache implements RealmCache {
|
|||
@Override
|
||||
public void invalidateCachedRealm(CachedRealm realm) {
|
||||
logger.tracev("Invalidating realm {0}", realm.getId());
|
||||
invalidateObject(realm.getId(), realmCounter);
|
||||
invalidateObject(realm.getId());
|
||||
realmLookup.remove(realm.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRealmById(String id) {
|
||||
CachedRealm cached = (CachedRealm) invalidateObject(id, realmCounter);
|
||||
CachedRealm cached = (CachedRealm) invalidateObject(id);
|
||||
if (cached != null) realmLookup.remove(cached.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedRealm(CachedRealm realm) {
|
||||
logger.tracev("Adding realm {0}", realm.getId());
|
||||
addRevisioned(realm.getId(), (Revisioned) realm, realmCounter);
|
||||
addRevisioned(realm.getId(), (Revisioned) realm);
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
}
|
||||
|
||||
|
@ -175,18 +172,18 @@ public class LockingRealmCache implements RealmCache {
|
|||
@Override
|
||||
public void invalidateApplication(CachedClient app) {
|
||||
logger.tracev("Removing application {0}", app.getId());
|
||||
invalidateObject(app.getId(), clientCounter);
|
||||
invalidateObject(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedClient(CachedClient app) {
|
||||
logger.tracev("Adding application {0}", app.getId());
|
||||
addRevisioned(app.getId(), (Revisioned) app, clientCounter);
|
||||
addRevisioned(app.getId(), (Revisioned) app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedApplicationById(String id) {
|
||||
CachedClient client = (CachedClient)invalidateObject(id, clientCounter);
|
||||
CachedClient client = (CachedClient)invalidateObject(id);
|
||||
if (client != null) logger.tracev("Removing application {0}", client.getClientId());
|
||||
}
|
||||
|
||||
|
@ -204,26 +201,26 @@ public class LockingRealmCache implements RealmCache {
|
|||
@Override
|
||||
public void invalidateGroup(CachedGroup role) {
|
||||
logger.tracev("Removing group {0}", role.getId());
|
||||
invalidateObject(role.getId(), groupCounter);
|
||||
invalidateObject(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedGroup(CachedGroup role) {
|
||||
logger.tracev("Adding group {0}", role.getId());
|
||||
addRevisioned(role.getId(), (Revisioned) role, groupCounter);
|
||||
addRevisioned(role.getId(), (Revisioned) role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
invalidateObject(id, groupCounter);
|
||||
invalidateObject(id);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
invalidateObject(id, groupCounter);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -234,13 +231,13 @@ public class LockingRealmCache implements RealmCache {
|
|||
@Override
|
||||
public void invalidateRole(CachedRole role) {
|
||||
logger.tracev("Removing role {0}", role.getId());
|
||||
invalidateObject(role.getId(), roleCounter);
|
||||
invalidateObject(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
invalidateObject(id, roleCounter);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -252,13 +249,13 @@ public class LockingRealmCache implements RealmCache {
|
|||
@Override
|
||||
public void addCachedRole(CachedRole role) {
|
||||
logger.tracev("Adding role {0}", role.getId());
|
||||
addRevisioned(role.getId(), (Revisioned) role, roleCounter);
|
||||
addRevisioned(role.getId(), (Revisioned) role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
invalidateObject(id, roleCounter);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -269,19 +266,19 @@ public class LockingRealmCache implements RealmCache {
|
|||
@Override
|
||||
public void invalidateClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Removing client template {0}", app.getId());
|
||||
invalidateObject(app.getId(), clientTemplateCounter);
|
||||
invalidateObject(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Adding client template {0}", app.getId());
|
||||
addRevisioned(app.getId(), (Revisioned) app, clientTemplateCounter);
|
||||
addRevisioned(app.getId(), (Revisioned) app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedClientTemplateById(String id) {
|
||||
logger.tracev("Removing client template {0}", id);
|
||||
invalidateObject(id, clientTemplateCounter);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.cache.infinispan.counter;
|
||||
package org.keycloak.models.cache.infinispan.locking;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.cache.infinispan.counter;
|
||||
package org.keycloak.models.cache.infinispan.locking;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.cache.infinispan.counter.entities;
|
||||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -6,8 +6,7 @@ import org.keycloak.models.RealmProvider;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models.cache.infinispan.counter.entities;
|
||||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,11 +1,11 @@
|
|||
package org.keycloak.models.cache.infinispan.counter.entities;
|
||||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models.cache.infinispan.counter.entities;
|
||||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.cache.infinispan.counter.entities;
|
||||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
|
@ -6,12 +6,8 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models.cache.infinispan.counter.entities;
|
||||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,9 +1,9 @@
|
|||
package org.keycloak.models.cache.infinispan.counter.entities;
|
||||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.counter.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.skewed;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.CacheRealmProviderFactory;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class RepeatableReadWriteSkewCacheRealmProviderFactory implements CacheRealmProviderFactory {
|
||||
|
||||
private static final Logger log = Logger.getLogger(RepeatableReadWriteSkewCacheRealmProviderFactory.class);
|
||||
|
||||
protected volatile RepeatableReadWriteSkewRealmCache realmCache;
|
||||
|
||||
protected final ConcurrentHashMap<String, String> realmLookup = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public CacheRealmProvider create(KeycloakSession session) {
|
||||
lazyInit(session);
|
||||
return new RepeatableReadWriteSkewRealmCacheProvider(realmCache, session);
|
||||
}
|
||||
|
||||
private void lazyInit(KeycloakSession session) {
|
||||
if (realmCache == null) {
|
||||
synchronized (this) {
|
||||
if (realmCache == null) {
|
||||
Cache<String, Object> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
|
||||
cache.addListener(new CacheListener());
|
||||
realmCache = new RepeatableReadWriteSkewRealmCache(cache, realmLookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "infinispan-versioned";
|
||||
}
|
||||
|
||||
@Listener
|
||||
public class CacheListener {
|
||||
|
||||
@CacheEntryCreated
|
||||
public void created(CacheEntryCreatedEvent<String, Object> event) {
|
||||
if (!event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
log.tracev("Realm added realm={0}", realm.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryRemoved
|
||||
public void removed(CacheEntryRemovedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryInvalidated
|
||||
public void removed(CacheEntryInvalidatedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntriesEvicted
|
||||
public void userEvicted(CacheEntriesEvictedEvent<String, Object> event) {
|
||||
for (Object object : event.getEntries().values()) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(Object object) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
|
||||
realmLookup.remove(realm.getName());
|
||||
|
||||
for (String r : realm.getRealmRoles().values()) {
|
||||
realmCache.evictCachedRoleById(r);
|
||||
}
|
||||
|
||||
for (String c : realm.getClients().values()) {
|
||||
realmCache.evictCachedApplicationById(c);
|
||||
}
|
||||
|
||||
log.tracev("Realm removed realm={0}", realm.getName());
|
||||
} else if (object instanceof CachedClient) {
|
||||
CachedClient client = (CachedClient) object;
|
||||
|
||||
for (String r : client.getRoles().values()) {
|
||||
realmCache.evictCachedRoleById(r);
|
||||
}
|
||||
|
||||
log.tracev("Client removed client={0}", client.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,164 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.skewed;
|
||||
|
||||
import org.infinispan.configuration.cache.CacheMode;
|
||||
import org.infinispan.configuration.cache.Configuration;
|
||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||
import org.infinispan.configuration.cache.VersioningScheme;
|
||||
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
|
||||
import org.infinispan.manager.DefaultCacheManager;
|
||||
import org.infinispan.manager.EmbeddedCacheManager;
|
||||
import org.infinispan.transaction.lookup.DummyTransactionManagerLookup;
|
||||
import org.infinispan.util.concurrent.IsolationLevel;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProvider;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class RepeatableReadWriteSkewConnectionProviderFactory implements InfinispanConnectionProviderFactory {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(RepeatableReadWriteSkewConnectionProviderFactory.class);
|
||||
|
||||
private Config.Scope config;
|
||||
|
||||
private EmbeddedCacheManager cacheManager;
|
||||
|
||||
private boolean containerManaged;
|
||||
|
||||
@Override
|
||||
public InfinispanConnectionProvider create(KeycloakSession session) {
|
||||
lazyInit();
|
||||
|
||||
return new DefaultInfinispanConnectionProvider(cacheManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (cacheManager != null && !containerManaged) {
|
||||
cacheManager.stop();
|
||||
}
|
||||
cacheManager = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "versioned";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
private void lazyInit() {
|
||||
if (cacheManager == null) {
|
||||
synchronized (this) {
|
||||
if (cacheManager == null) {
|
||||
String cacheContainer = config.get("cacheContainer");
|
||||
if (cacheContainer != null) {
|
||||
initContainerManaged(cacheContainer);
|
||||
} else {
|
||||
initEmbedded();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initContainerManaged(String cacheContainerLookup) {
|
||||
try {
|
||||
cacheManager = (EmbeddedCacheManager) new InitialContext().lookup(cacheContainerLookup);
|
||||
containerManaged = true;
|
||||
|
||||
logger.debugv("Using container managed Infinispan cache container, lookup={1}", cacheContainerLookup);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to retrieve cache container", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void initEmbedded() {
|
||||
GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder();
|
||||
|
||||
boolean clustered = config.getBoolean("clustered", false);
|
||||
boolean async = config.getBoolean("async", true);
|
||||
boolean allowDuplicateJMXDomains = config.getBoolean("allowDuplicateJMXDomains", true);
|
||||
|
||||
if (clustered) {
|
||||
gcb.transport().defaultTransport();
|
||||
}
|
||||
gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains);
|
||||
|
||||
cacheManager = new DefaultCacheManager(gcb.build());
|
||||
containerManaged = false;
|
||||
|
||||
logger.debug("Started embedded Infinispan cache container");
|
||||
|
||||
ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder();
|
||||
if (clustered) {
|
||||
invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC);
|
||||
}
|
||||
|
||||
invalidationConfigBuilder.transaction().transactionManagerLookup(new DummyTransactionManagerLookup());
|
||||
invalidationConfigBuilder.locking().isolationLevel(IsolationLevel.REPEATABLE_READ).writeSkewCheck(true).versioning().enable().scheme(VersioningScheme.SIMPLE);
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationConfigBuilder.build());
|
||||
|
||||
ConfigurationBuilder userConfigBuilder = new ConfigurationBuilder();
|
||||
if (clustered) {
|
||||
userConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC);
|
||||
}
|
||||
Configuration userCacheConfiguration = userConfigBuilder.build();
|
||||
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.USER_CACHE_NAME, userCacheConfiguration);
|
||||
|
||||
ConfigurationBuilder sessionConfigBuilder = new ConfigurationBuilder();
|
||||
if (clustered) {
|
||||
String sessionsMode = config.get("sessionsMode", "distributed");
|
||||
if (sessionsMode.equalsIgnoreCase("replicated")) {
|
||||
sessionConfigBuilder.clustering().cacheMode(async ? CacheMode.REPL_ASYNC : CacheMode.REPL_SYNC);
|
||||
} else if (sessionsMode.equalsIgnoreCase("distributed")) {
|
||||
sessionConfigBuilder.clustering().cacheMode(async ? CacheMode.DIST_ASYNC : CacheMode.DIST_SYNC);
|
||||
} else {
|
||||
throw new RuntimeException("Invalid value for sessionsMode");
|
||||
}
|
||||
|
||||
sessionConfigBuilder.clustering().hash()
|
||||
.numOwners(config.getInt("sessionsOwners", 2))
|
||||
.numSegments(config.getInt("sessionsSegments", 60)).build();
|
||||
}
|
||||
|
||||
Configuration sessionCacheConfiguration = sessionConfigBuilder.build();
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.SESSION_CACHE_NAME, sessionCacheConfiguration);
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.OFFLINE_SESSION_CACHE_NAME, sessionCacheConfiguration);
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.LOGIN_FAILURE_CACHE_NAME, sessionCacheConfiguration);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,269 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.skewed;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
import javax.transaction.NotSupportedException;
|
||||
import javax.transaction.Status;
|
||||
import javax.transaction.SystemException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class RepeatableReadWriteSkewRealmCache implements RealmCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(RepeatableReadWriteSkewRealmCache.class);
|
||||
|
||||
protected final Cache<String, Object> cache;
|
||||
protected final ConcurrentHashMap<String, String> realmLookup;
|
||||
|
||||
public RepeatableReadWriteSkewRealmCache(Cache<String, Object> cache, ConcurrentHashMap<String, String> realmLookup) {
|
||||
this.cache = cache;
|
||||
this.realmLookup = realmLookup;
|
||||
}
|
||||
|
||||
public Cache<String, Object> getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void startBatch() {
|
||||
logger.trace("*** START BATCH ***");
|
||||
try {
|
||||
if (cache.getAdvancedCache().getTransactionManager().getStatus() == Status.STATUS_NO_TRANSACTION) {
|
||||
cache.getAdvancedCache().getTransactionManager().begin();
|
||||
}
|
||||
} catch (NotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (SystemException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void endBatch(boolean commit) {
|
||||
logger.trace("*** END BATCH ***");
|
||||
try {
|
||||
if (cache.getAdvancedCache().getTransactionManager().getStatus() == Status.STATUS_ACTIVE) {
|
||||
if (commit) {
|
||||
cache.getAdvancedCache().getTransactionManager().commit();
|
||||
|
||||
} else {
|
||||
cache.getAdvancedCache().getTransactionManager().rollback();
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getCachedRealm(String id) {
|
||||
return get(id, CachedRealm.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRealm(CachedRealm realm) {
|
||||
logger.tracev("Invalidating realm {0}", realm.getId());
|
||||
invalidate(realm.getId());
|
||||
realmLookup.remove(realm.getName());
|
||||
}
|
||||
|
||||
protected Object invalidate(String id) {
|
||||
startBatch();
|
||||
Object rtn = cache.remove(id);
|
||||
logger.trace("*** END BATCH ***");
|
||||
try {
|
||||
if (cache.getAdvancedCache().getTransactionManager().getStatus() == Status.STATUS_ACTIVE) {
|
||||
if (true) {
|
||||
cache.getAdvancedCache().getTransactionManager().commit();
|
||||
|
||||
} else {
|
||||
cache.getAdvancedCache().getTransactionManager().rollback();
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.trace("Failed to commit invalidate");
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRealmById(String id) {
|
||||
CachedRealm cached = (CachedRealm) invalidate(id);
|
||||
if (cached != null) realmLookup.remove(cached.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedRealm(CachedRealm realm) {
|
||||
logger.tracev("Adding realm {0}", realm.getId());
|
||||
cache.putForExternalRead(realm.getId(), realm);
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getCachedRealmByName(String name) {
|
||||
String id = realmLookup.get(name);
|
||||
return id != null ? getCachedRealm(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClient getApplication(String id) {
|
||||
return get(id, CachedClient.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateApplication(CachedClient app) {
|
||||
logger.tracev("Removing application {0}", app.getId());
|
||||
invalidate(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedClient(CachedClient app) {
|
||||
logger.tracev("Adding application {0}", app.getId());
|
||||
cache.putForExternalRead(app.getId(), app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedApplicationById(String id) {
|
||||
logger.tracev("Removing application {0}", id);
|
||||
invalidate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictCachedApplicationById(String id) {
|
||||
logger.tracev("Evicting application {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedGroup getGroup(String id) {
|
||||
return get(id, CachedGroup.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroup(CachedGroup role) {
|
||||
logger.tracev("Removing group {0}", role.getId());
|
||||
invalidate(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedGroup(CachedGroup role) {
|
||||
logger.tracev("Adding group {0}", role.getId());
|
||||
cache.putForExternalRead(role.getId(), role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
invalidate(id);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
invalidate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRole getRole(String id) {
|
||||
return get(id, CachedRole.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRole(CachedRole role) {
|
||||
logger.tracev("Removing role {0}", role.getId());
|
||||
invalidate(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
invalidate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictCachedRoleById(String id) {
|
||||
logger.tracev("Evicting role {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedRole(CachedRole role) {
|
||||
logger.tracev("Adding role {0}", role.getId());
|
||||
cache.putForExternalRead(role.getId(), role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
invalidate(id);
|
||||
}
|
||||
|
||||
private <T> T get(String id, Class<T> type) {
|
||||
Object o = cache.get(id);
|
||||
return o != null && type.isInstance(o) ? type.cast(o) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClientTemplate getClientTemplate(String id) {
|
||||
return get(id, CachedClientTemplate.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Removing client template {0}", app.getId());
|
||||
invalidate(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCachedClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Adding client template {0}", app.getId());
|
||||
cache.putForExternalRead(app.getId(), app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateCachedClientTemplateById(String id) {
|
||||
logger.tracev("Removing client template {0}", id);
|
||||
invalidate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictCachedClientTemplateById(String id) {
|
||||
logger.tracev("Evicting client template {0}", id);
|
||||
invalidate(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,472 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 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.cache.infinispan.skewed;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.ClientAdapter;
|
||||
import org.keycloak.models.cache.infinispan.ClientTemplateAdapter;
|
||||
import org.keycloak.models.cache.infinispan.GroupAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RealmAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RoleAdapter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* DO NOT USE THIS!!
|
||||
*
|
||||
* Tries unsuccessfully to use Infinispan with REPEATABLE_READ, write-skew-checking
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RepeatableReadWriteSkewRealmCacheProvider implements CacheRealmProvider {
|
||||
protected static final Logger logger = Logger.getLogger(RepeatableReadWriteSkewRealmCacheProvider.class);
|
||||
|
||||
protected RepeatableReadWriteSkewRealmCache cache;
|
||||
protected KeycloakSession session;
|
||||
protected RealmProvider delegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
|
||||
protected Set<String> realmInvalidations = new HashSet<>();
|
||||
protected Set<String> appInvalidations = new HashSet<>();
|
||||
protected Set<String> clientTemplateInvalidations = new HashSet<>();
|
||||
protected Set<String> roleInvalidations = new HashSet<>();
|
||||
protected Set<String> groupInvalidations = new HashSet<>();
|
||||
protected Map<String, RealmModel> managedRealms = new HashMap<>();
|
||||
protected Map<String, ClientModel> managedApplications = new HashMap<>();
|
||||
protected Map<String, ClientTemplateModel> managedClientTemplates = new HashMap<>();
|
||||
protected Map<String, RoleModel> managedRoles = new HashMap<>();
|
||||
protected Map<String, GroupModel> managedGroups = new HashMap<>();
|
||||
|
||||
protected boolean clearAll;
|
||||
|
||||
public RepeatableReadWriteSkewRealmCacheProvider(RepeatableReadWriteSkewRealmCache cache, KeycloakSession session) {
|
||||
this.cache = cache;
|
||||
this.session = session;
|
||||
|
||||
session.getTransaction().enlistAfterCompletion(getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider getDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(RealmProvider.class);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRealmInvalidation(String id) {
|
||||
realmInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerApplicationInvalidation(String id) {
|
||||
appInvalidations.add(id);
|
||||
}
|
||||
@Override
|
||||
public void registerClientTemplateInvalidation(String id) {
|
||||
clientTemplateInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRoleInvalidation(String id) {
|
||||
roleInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGroupInvalidation(String id) {
|
||||
groupInvalidations.add(id);
|
||||
|
||||
}
|
||||
|
||||
protected void runInvalidations() {
|
||||
for (String id : realmInvalidations) {
|
||||
cache.invalidateCachedRealmById(id);
|
||||
}
|
||||
for (String id : roleInvalidations) {
|
||||
cache.invalidateRoleById(id);
|
||||
}
|
||||
for (String id : groupInvalidations) {
|
||||
cache.invalidateGroupById(id);
|
||||
}
|
||||
for (String id : appInvalidations) {
|
||||
cache.invalidateCachedApplicationById(id);
|
||||
}
|
||||
for (String id : clientTemplateInvalidations) {
|
||||
cache.invalidateCachedClientTemplateById(id);
|
||||
}
|
||||
}
|
||||
|
||||
private KeycloakTransaction getTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
setRollbackOnly = true;
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
RealmModel realm = getDelegate().createRealm(name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmModel realm = getDelegate().createRealm(id, name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
//cache.startBatch();
|
||||
cache.startBatch();
|
||||
boolean batchEnded = false;
|
||||
try {
|
||||
CachedRealm cached = cache.getCachedRealm(id);
|
||||
boolean wasNull = cached == null;
|
||||
if (cached == null) {
|
||||
RealmModel model = getDelegate().getRealm(id);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(id)) return model;
|
||||
cached = new CachedRealm(cache, this, model);
|
||||
cache.addCachedRealm(cached);
|
||||
try {
|
||||
batchEnded = true;
|
||||
cache.endBatch(true);
|
||||
logger.trace("returning new cached realm");
|
||||
} catch (Exception exception) {
|
||||
logger.trace("failed to add to cache", exception);
|
||||
return model;
|
||||
}
|
||||
} else if (realmInvalidations.contains(id)) {
|
||||
return getDelegate().getRealm(id);
|
||||
} else if (managedRealms.containsKey(id)) {
|
||||
return managedRealms.get(id);
|
||||
}
|
||||
if (!wasNull) logger.trace("returning cached realm: " + cached.getName());
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(id, adapter);
|
||||
return adapter;
|
||||
} finally {
|
||||
if (!batchEnded) cache.endBatch(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealmByName(String name) {
|
||||
cache.startBatch();
|
||||
boolean batchEnded = false;
|
||||
try {
|
||||
CachedRealm cached = cache.getCachedRealmByName(name);
|
||||
boolean wasNull = cached == null;
|
||||
if (cached == null) {
|
||||
RealmModel model = getDelegate().getRealmByName(name);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(model.getId())) return model;
|
||||
cached = new CachedRealm(cache, this, model);
|
||||
cache.addCachedRealm(cached);
|
||||
try {
|
||||
batchEnded = true;
|
||||
cache.endBatch(true);
|
||||
logger.trace("returning new cached realm: " + cached.getName());
|
||||
} catch (Exception exception) {
|
||||
logger.trace("failed to add to cache", exception);
|
||||
return model;
|
||||
}
|
||||
} else if (realmInvalidations.contains(cached.getId())) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
} else if (managedRealms.containsKey(cached.getId())) {
|
||||
return managedRealms.get(cached.getId());
|
||||
}
|
||||
if (!wasNull) logger.trace("returning cached realm: " + cached.getName());
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(cached.getId(), adapter);
|
||||
return adapter;
|
||||
} finally {
|
||||
if (!batchEnded) cache.endBatch(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
// Retrieve realms from backend
|
||||
List<RealmModel> backendRealms = getDelegate().getRealms();
|
||||
|
||||
// Return cache delegates to ensure cache invalidated during write operations
|
||||
List<RealmModel> cachedRealms = new LinkedList<RealmModel>();
|
||||
for (RealmModel realm : backendRealms) {
|
||||
RealmModel cached = getRealm(realm.getId());
|
||||
cachedRealms.add(cached);
|
||||
}
|
||||
return cachedRealms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRealm(String id) {
|
||||
cache.invalidateCachedRealmById(id);
|
||||
|
||||
RealmModel realm = getDelegate().getRealm(id);
|
||||
Set<RoleModel> realmRoles = null;
|
||||
if (realm != null) {
|
||||
realmRoles = realm.getRoles();
|
||||
}
|
||||
|
||||
boolean didIt = getDelegate().removeRealm(id);
|
||||
realmInvalidations.add(id);
|
||||
|
||||
// TODO: Temporary workaround to invalidate cached realm roles
|
||||
if (didIt && realmRoles != null) {
|
||||
for (RoleModel role : realmRoles) {
|
||||
roleInvalidations.add(role.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return didIt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null) delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id, RealmModel realm) {
|
||||
cache.startBatch();
|
||||
boolean batchEnded = false;
|
||||
try {
|
||||
CachedRole cached = cache.getRole(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
RoleModel model = getDelegate().getRoleById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (roleInvalidations.contains(id)) return model;
|
||||
if (model.getContainer() instanceof ClientModel) {
|
||||
cached = new CachedClientRole(((ClientModel) model.getContainer()).getId(), model, realm);
|
||||
} else {
|
||||
cached = new CachedRealmRole(model, realm);
|
||||
}
|
||||
cache.addCachedRole(cached);
|
||||
try {
|
||||
batchEnded = true;
|
||||
cache.endBatch(true);
|
||||
} catch (Exception exception) {
|
||||
logger.trace("failed to add to cache", exception);
|
||||
return model;
|
||||
}
|
||||
|
||||
} else if (roleInvalidations.contains(id)) {
|
||||
return getDelegate().getRoleById(id, realm);
|
||||
} else if (managedRoles.containsKey(id)) {
|
||||
return managedRoles.get(id);
|
||||
}
|
||||
RoleAdapter adapter = new RoleAdapter(cached, cache, this, realm);
|
||||
managedRoles.put(id, adapter);
|
||||
return adapter;
|
||||
} finally {
|
||||
if (!batchEnded) cache.endBatch(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||
cache.startBatch();
|
||||
boolean batchEnded = false;
|
||||
try {
|
||||
CachedGroup cached = cache.getGroup(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (groupInvalidations.contains(id)) return model;
|
||||
cached = new CachedGroup(realm, model);
|
||||
cache.addCachedGroup(cached);
|
||||
try {
|
||||
batchEnded = true;
|
||||
cache.endBatch(true);
|
||||
} catch (Exception exception) {
|
||||
logger.trace("failed to add to cache", exception);
|
||||
return model;
|
||||
}
|
||||
|
||||
} else if (groupInvalidations.contains(id)) {
|
||||
return getDelegate().getGroupById(id, realm);
|
||||
} else if (managedGroups.containsKey(id)) {
|
||||
return managedGroups.get(id);
|
||||
}
|
||||
GroupAdapter adapter = new GroupAdapter(cached, this, session, realm);
|
||||
managedGroups.put(id, adapter);
|
||||
return adapter;
|
||||
} finally {
|
||||
if (!batchEnded) cache.endBatch(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
cache.startBatch();
|
||||
boolean batchEnded = false;
|
||||
CachedClient cached = cache.getApplication(id);
|
||||
try {
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (appInvalidations.contains(id)) return model;
|
||||
cached = new CachedClient(cache, getDelegate(), realm, model);
|
||||
cache.addCachedClient(cached);
|
||||
try {
|
||||
batchEnded = true;
|
||||
cache.endBatch(true);
|
||||
} catch (Exception exception) {
|
||||
logger.trace("failed to add to cache", exception);
|
||||
return model;
|
||||
}
|
||||
} else if (appInvalidations.contains(id)) {
|
||||
return getDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
return managedApplications.get(id);
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache);
|
||||
managedApplications.put(id, adapter);
|
||||
return adapter;
|
||||
} finally {
|
||||
if (!batchEnded) cache.endBatch(true);
|
||||
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
|
||||
cache.startBatch();
|
||||
boolean batchEnded = false;
|
||||
try {
|
||||
CachedClientTemplate cached = cache.getClientTemplate(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (clientTemplateInvalidations.contains(id)) return model;
|
||||
cached = new CachedClientTemplate(cache, getDelegate(), realm, model);
|
||||
cache.addCachedClientTemplate(cached);
|
||||
try {
|
||||
batchEnded = true;
|
||||
cache.endBatch(true);
|
||||
} catch (Exception exception) {
|
||||
logger.trace("failed to add to cache", exception);
|
||||
return model;
|
||||
}
|
||||
} else if (clientTemplateInvalidations.contains(id)) {
|
||||
return getDelegate().getClientTemplateById(id, realm);
|
||||
} else if (managedClientTemplates.containsKey(id)) {
|
||||
return managedClientTemplates.get(id);
|
||||
}
|
||||
ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this, cache);
|
||||
managedClientTemplates.put(id, adapter);
|
||||
return adapter;
|
||||
} finally {
|
||||
if (!batchEnded) cache.endBatch(true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -16,5 +16,4 @@
|
|||
#
|
||||
|
||||
org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory
|
||||
org.keycloak.models.cache.infinispan.counter.RevisionedConnectionProviderFactory
|
||||
org.keycloak.models.cache.infinispan.locking.LockingConnectionProviderFactory
|
|
@ -16,5 +16,4 @@
|
|||
#
|
||||
|
||||
org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory
|
||||
org.keycloak.models.cache.infinispan.counter.RevisionedCacheRealmProviderFactory
|
||||
org.keycloak.models.cache.infinispan.locking.LockingCacheRealmProviderFactory
|
|
@ -46,8 +46,8 @@ public class ConcurrencyTest extends AbstractClientTest {
|
|||
|
||||
private static final Logger log = Logger.getLogger(ConcurrencyTest.class);
|
||||
|
||||
private static final int DEFAULT_THREADS = 1;
|
||||
private static final int DEFAULT_ITERATIONS = 5;
|
||||
private static final int DEFAULT_THREADS = 5;
|
||||
private static final int DEFAULT_ITERATIONS = 20;
|
||||
|
||||
// If enabled only one request is allowed at the time. Useful for checking that test is working.
|
||||
private static final boolean SYNCHRONIZED = false;
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
<module>tomcat8</module>
|
||||
<module>jetty</module>
|
||||
<module>performance</module>
|
||||
<module>stress</module>
|
||||
<module>integration-arquillian</module>
|
||||
</modules>
|
||||
|
||||
|
|
571
testsuite/stress/pom.xml
Executable file
571
testsuite/stress/pom.xml
Executable file
|
@ -0,0 +1,571 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-testsuite-pom</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.9.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-stress-tester</artifactId>
|
||||
<name>Keycloak Stress TestSuite</name>
|
||||
<description />
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-dependencies-server-all</artifactId>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-admin-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-wildfly-adduser</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.servlet</groupId>
|
||||
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.ws.rs</groupId>
|
||||
<artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>async-http-servlet-3.0</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-undertow</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-multipart-provider</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jackson2-provider</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.zxing</groupId>
|
||||
<artifactId>javase</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-ldap-federation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-kerberos-federation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-adapter-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-servlet-filter-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-undertow-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jaxrs-oauth-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>federation-properties-example</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-servlet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
<artifactId>hibernate-jpa-2.1-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.icegreen</groupId>
|
||||
<artifactId>greenmail</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-java</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>xml-apis</groupId>
|
||||
<artifactId>xml-apis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.seleniumhq.selenium</groupId>
|
||||
<artifactId>selenium-chrome-driver</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache DS -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-util-embedded-ldap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-undertow</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-testsuite-integration</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${maven.compiler.source}</source>
|
||||
<target>${maven.compiler.target}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<workingDirectory>${project.basedir}</workingDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<inherited>true</inherited>
|
||||
<extensions>true</extensions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>keycloak-server</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<mainClass>org.keycloak.testsuite.KeycloakServer</mainClass>
|
||||
<classpathScope>test</classpathScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>mail-server</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<mainClass>org.keycloak.testsuite.MailServer</mainClass>
|
||||
<classpathScope>test</classpathScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>totp</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<mainClass>org.keycloak.testsuite.TotpGenerator</mainClass>
|
||||
<classpathScope>test</classpathScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>ldap</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<mainClass>org.keycloak.util.ldap.LDAPEmbeddedServer</mainClass>
|
||||
<classpathScope>test</classpathScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>kerberos</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<mainClass>org.keycloak.util.ldap.KerberosEmbeddedServer</mainClass>
|
||||
<classpathScope>test</classpathScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>jpa</id>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<keycloak.realm.provider>jpa</keycloak.realm.provider>
|
||||
<keycloak.user.provider>jpa</keycloak.user.provider>
|
||||
<keycloak.userSessionPersister.provider>jpa</keycloak.userSessionPersister.provider>
|
||||
<keycloak.eventsStore.provider>jpa</keycloak.eventsStore.provider>
|
||||
|
||||
<keycloak.liquibase.logging.level>debug</keycloak.liquibase.logging.level>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>mongo</id>
|
||||
|
||||
<properties>
|
||||
<keycloak.connectionsMongo.host>localhost</keycloak.connectionsMongo.host>
|
||||
<keycloak.connectionsMongo.port>27018</keycloak.connectionsMongo.port>
|
||||
<keycloak.connectionsMongo.db>keycloak</keycloak.connectionsMongo.db>
|
||||
<keycloak.connectionsMongo.bindIp>127.0.0.1</keycloak.connectionsMongo.bindIp>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>test</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<systemPropertyVariables>
|
||||
<keycloak.realm.provider>mongo</keycloak.realm.provider>
|
||||
<keycloak.user.provider>mongo</keycloak.user.provider>
|
||||
<keycloak.userSessionPersister.provider>mongo</keycloak.userSessionPersister.provider>
|
||||
<keycloak.eventsStore.provider>mongo</keycloak.eventsStore.provider>
|
||||
<keycloak.connectionsMongo.host>${keycloak.connectionsMongo.host}</keycloak.connectionsMongo.host>
|
||||
<keycloak.connectionsMongo.port>${keycloak.connectionsMongo.port}</keycloak.connectionsMongo.port>
|
||||
<keycloak.connectionsMongo.db>${keycloak.connectionsMongo.db}</keycloak.connectionsMongo.db>
|
||||
<keycloak.connectionsMongo.bindIp>${keycloak.connectionsMongo.bindIp}</keycloak.connectionsMongo.bindIp>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>default-test</id>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Embedded mongo -->
|
||||
<plugin>
|
||||
<groupId>com.github.joelittlejohn.embedmongo</groupId>
|
||||
<artifactId>embedmongo-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>start-mongodb</id>
|
||||
<phase>pre-integration-test</phase>
|
||||
<goals>
|
||||
<goal>start</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<port>${keycloak.connectionsMongo.port}</port>
|
||||
<logging>file</logging>
|
||||
<logFile>${project.build.directory}/mongodb.log</logFile>
|
||||
<bindIp>${keycloak.connectionsMongo.bindIp}</bindIp>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>stop-mongodb</id>
|
||||
<phase>post-integration-test</phase>
|
||||
<goals>
|
||||
<goal>stop</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</profile>
|
||||
|
||||
<!-- MySQL -->
|
||||
<profile>
|
||||
<activation>
|
||||
<property>
|
||||
<name>keycloak.connectionsJpa.driver</name>
|
||||
<value>com.mysql.jdbc.Driver</value>
|
||||
</property>
|
||||
</activation>
|
||||
<id>mysql</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<!-- PostgreSQL -->
|
||||
<profile>
|
||||
<activation>
|
||||
<property>
|
||||
<name>keycloak.connectionsJpa.driver</name>
|
||||
<value>org.postgresql.Driver</value>
|
||||
</property>
|
||||
</activation>
|
||||
<id>postgresql</id>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgresql.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>clean-jpa</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.liquibase</groupId>
|
||||
<artifactId>liquibase-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
|
||||
|
||||
<url>${keycloak.connectionsJpa.url}</url>
|
||||
<driver>${keycloak.connectionsJpa.driver}</driver>
|
||||
<username>${keycloak.connectionsJpa.user}</username>
|
||||
<password>${keycloak.connectionsJpa.password}</password>
|
||||
|
||||
<promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
|
||||
<databaseClass>${keycloak.connectionsJpa.liquibaseDatabaseClass}</databaseClass>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>clean-jpa</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>dropAll</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<!-- Ldap profiles -->
|
||||
<profile>
|
||||
<activation>
|
||||
<property>
|
||||
<name>ldap.vendor</name>
|
||||
<value>msad</value>
|
||||
</property>
|
||||
</activation>
|
||||
<id>msad</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>org/keycloak/testsuite/federation/ldap/base/**</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/LDAPMultipleAttributesTest.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
</project>
|
137
testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java
Executable file
137
testsuite/stress/src/main/java/org/keycloak/test/stress/MaxRateExecutor.java
Executable file
|
@ -0,0 +1,137 @@
|
|||
package org.keycloak.test.stress;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Executes all test threads until completion.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class MaxRateExecutor {
|
||||
|
||||
public static class RateResult {
|
||||
StressResult result;
|
||||
int threads;
|
||||
long time;
|
||||
|
||||
public RateResult(StressResult result, int threads, long time) {
|
||||
this.result = result;
|
||||
this.threads = threads;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public StressResult getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getThreads() {
|
||||
return threads;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
List<RateResult> allResults = new LinkedList<>();
|
||||
RateResult fastest = null;
|
||||
RateResult last = null;
|
||||
|
||||
|
||||
|
||||
public void best(TestFactory factory, int jobs) {
|
||||
fastest = last = null;
|
||||
int threads = 2;
|
||||
do {
|
||||
fastest = last;
|
||||
try {
|
||||
last = execute(factory, threads, jobs);
|
||||
allResults.add(last);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
threads++;
|
||||
} while (fastest == null || fastest.time > last.time);
|
||||
}
|
||||
|
||||
public RateResult getFastest() {
|
||||
return fastest;
|
||||
}
|
||||
|
||||
public RateResult getLast() {
|
||||
return last;
|
||||
}
|
||||
|
||||
public RateResult execute(TestFactory factory, int threads, int jobs) throws InterruptedException, ExecutionException {
|
||||
List<StressTest> tests = new LinkedList<>();
|
||||
ExecutorService executor = Executors.newFixedThreadPool(threads);
|
||||
ExecutorCompletionService<StressTest> completionService = new ExecutorCompletionService<>(executor);
|
||||
StressResult result = new StressResult("num threads:" + threads);
|
||||
addTest(factory, result, tests, threads + 5);
|
||||
long start = System.currentTimeMillis();
|
||||
for (StressTest stressTest : tests) {
|
||||
completionService.submit(stressTest);
|
||||
}
|
||||
for (int i = 0; i < jobs; i++) {
|
||||
Future<StressTest> future = completionService.take();
|
||||
StressTest stressTest = future.get();
|
||||
if (i < jobs - threads - 5) completionService.submit(stressTest);
|
||||
}
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(10, TimeUnit.SECONDS);
|
||||
long end = System.currentTimeMillis() - start;
|
||||
RateResult rate = new RateResult(result, threads, end);
|
||||
return rate;
|
||||
}
|
||||
|
||||
private void addTest(TestFactory factory, StressResult result, List<StressTest> tests, int num) {
|
||||
int add = num - tests.size();
|
||||
for (int i = 0; i < add; i++) {
|
||||
Test test = factory.create();
|
||||
test.init();
|
||||
StressTest stress = new StressTest(result, test, 1);
|
||||
tests.add(stress);
|
||||
}
|
||||
}
|
||||
|
||||
public void printResults() {
|
||||
System.out.println("*******************");
|
||||
System.out.println("* Best Result *");
|
||||
System.out.println("*******************");
|
||||
printResult(fastest);
|
||||
}
|
||||
|
||||
|
||||
public void printResult(RateResult result) {
|
||||
System.out.println("Threads: " + result.getThreads());
|
||||
System.out.println("Total Time: " + result.getTime());
|
||||
System.out.println("Successes: " + result.getResult().getSuccess());
|
||||
System.out.println("Iterations: " + result.getResult().getIterations());
|
||||
System.out.println("Average time: " + result.getResult().getAverageTime());
|
||||
System.out.println("Rate: " + result.getResult().getRate());
|
||||
|
||||
}
|
||||
|
||||
public void printSummary() {
|
||||
|
||||
for (RateResult result : allResults) {
|
||||
System.out.println("*******************");
|
||||
printSummary();
|
||||
}
|
||||
}
|
||||
public void printSummary(RateResult result) {
|
||||
System.out.println("Threads: " + result.getThreads());
|
||||
System.out.println("Total Time: " + result.getTime());
|
||||
}
|
||||
}
|
65
testsuite/stress/src/main/java/org/keycloak/test/stress/StressExecutor.java
Executable file
65
testsuite/stress/src/main/java/org/keycloak/test/stress/StressExecutor.java
Executable file
|
@ -0,0 +1,65 @@
|
|||
package org.keycloak.test.stress;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Executes all test threads until completion.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class StressExecutor {
|
||||
protected List<StressTest> tests = new LinkedList<>();
|
||||
protected List<StressResult> results = new LinkedList<>();
|
||||
|
||||
public void addTest(Class<? extends Test> test, int threads, int iterations) {
|
||||
StressResult result = new StressResult(test.getName());
|
||||
results.add(result);
|
||||
for (int i = 0; i < threads; i++) {
|
||||
try {
|
||||
Test t = test.newInstance();
|
||||
t.init();
|
||||
StressTest stress = new StressTest(result, t, iterations);
|
||||
tests.add(stress);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addTest(Test test, StressResult result, int iterations) {
|
||||
tests.add(new StressTest(result, test, iterations));
|
||||
}
|
||||
|
||||
public void addTest(Test test, int iterations) {
|
||||
StressResult result = new StressResult(test.getClass().getName());
|
||||
tests.add(new StressTest(result, test, iterations));
|
||||
}
|
||||
|
||||
public long execute() throws InterruptedException, ExecutionException {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(tests.size());
|
||||
Collections.shuffle(tests);
|
||||
long start = System.currentTimeMillis();
|
||||
for (StressTest test : tests) {
|
||||
executor.submit(test);
|
||||
}
|
||||
executor.shutdown();
|
||||
boolean done = executor.awaitTermination(100, TimeUnit.HOURS);
|
||||
long end = System.currentTimeMillis() - start;
|
||||
return end;
|
||||
|
||||
}
|
||||
}
|
62
testsuite/stress/src/main/java/org/keycloak/test/stress/StressResult.java
Executable file
62
testsuite/stress/src/main/java/org/keycloak/test/stress/StressResult.java
Executable file
|
@ -0,0 +1,62 @@
|
|||
package org.keycloak.test.stress;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class StressResult {
|
||||
ThreadLocal<Long> start = new ThreadLocal<>();
|
||||
AtomicLong iterations = new AtomicLong();
|
||||
AtomicLong totalTime = new AtomicLong();
|
||||
String name;
|
||||
AtomicInteger success = new AtomicInteger();
|
||||
|
||||
public StressResult(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
start.set(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public void success() {
|
||||
success.incrementAndGet();
|
||||
}
|
||||
|
||||
public void end() {
|
||||
long end = System.currentTimeMillis() - start.get();
|
||||
totalTime.addAndGet(end);
|
||||
iterations.incrementAndGet();
|
||||
}
|
||||
|
||||
public int getSuccess() {
|
||||
return success.get();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public long getTotalTime() {
|
||||
return totalTime.longValue();
|
||||
}
|
||||
public long getIterations() {
|
||||
return iterations.get();
|
||||
}
|
||||
|
||||
public double getAverageTime() {
|
||||
return (double)(double)totalTime.get() / (double)iterations.get();
|
||||
}
|
||||
public double getRate() {
|
||||
return (double)(double)iterations.get() / (double)totalTime.get();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
iterations.set(0);
|
||||
totalTime.set(0);
|
||||
success.set(0);
|
||||
}
|
||||
}
|
37
testsuite/stress/src/main/java/org/keycloak/test/stress/StressTest.java
Executable file
37
testsuite/stress/src/main/java/org/keycloak/test/stress/StressTest.java
Executable file
|
@ -0,0 +1,37 @@
|
|||
package org.keycloak.test.stress;
|
||||
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class StressTest implements Callable<StressTest> {
|
||||
protected StressResult result;
|
||||
protected Callable<Boolean> test;
|
||||
protected int iterations;
|
||||
|
||||
public StressTest(StressResult result, Callable<Boolean> test, int iterations) {
|
||||
this.result = result;
|
||||
this.test = test;
|
||||
this.iterations = iterations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StressTest call() throws Exception {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
result.start();
|
||||
try {
|
||||
if (test.call()) {
|
||||
result.success();
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
}
|
||||
result.end();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
11
testsuite/stress/src/main/java/org/keycloak/test/stress/Test.java
Executable file
11
testsuite/stress/src/main/java/org/keycloak/test/stress/Test.java
Executable file
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.test.stress;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface Test extends Callable<Boolean> {
|
||||
void init();
|
||||
}
|
9
testsuite/stress/src/main/java/org/keycloak/test/stress/TestFactory.java
Executable file
9
testsuite/stress/src/main/java/org/keycloak/test/stress/TestFactory.java
Executable file
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.test.stress;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface TestFactory {
|
||||
Test create();
|
||||
}
|
104
testsuite/stress/src/main/java/org/keycloak/test/stress/tests/LoginLogout.java
Executable file
104
testsuite/stress/src/main/java/org/keycloak/test/stress/tests/LoginLogout.java
Executable file
|
@ -0,0 +1,104 @@
|
|||
package org.keycloak.test.stress.tests;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.test.stress.Test;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class LoginLogout implements Test {
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
protected String securedResourceUrl;
|
||||
protected List<String> containsInPage = new LinkedList<>();
|
||||
protected String realm;
|
||||
protected String authServerUrl;
|
||||
protected String username;
|
||||
protected String password;
|
||||
|
||||
protected String loginUrl;
|
||||
protected String logoutUrl;
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
public LoginLogout securedResourceUrl(String securedResourceUrl) {
|
||||
this.securedResourceUrl = securedResourceUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginLogout addPageContains(String contains) {
|
||||
containsInPage.add(contains);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginLogout realm(String realm) {
|
||||
this.realm = realm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginLogout authServerUrl(String authServerUrl) {
|
||||
this.authServerUrl = authServerUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginLogout username(String username) {
|
||||
this.username = username;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginLogout password(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
|
||||
loginUrl = OIDCLoginProtocolService.authUrl(UriBuilder.fromUri(authServerUrl)).build(realm).toString();
|
||||
logoutUrl = OIDCLoginProtocolService.logoutUrl(UriBuilder.fromUri(authServerUrl))
|
||||
.queryParam(OAuth2Constants.REDIRECT_URI, securedResourceUrl).build(realm).toString();
|
||||
try {
|
||||
webRule.before();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
driver.navigate().to(securedResourceUrl);
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(loginUrl));
|
||||
loginPage.login(username, password);
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(securedResourceUrl));
|
||||
String pageSource = driver.getPageSource();
|
||||
for (String contains : containsInPage) {
|
||||
Assert.assertTrue(pageSource.contains(contains));
|
||||
|
||||
}
|
||||
|
||||
// test logout
|
||||
driver.navigate().to(logoutUrl);
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith(loginUrl));
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2016 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.test;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CustomerDatabaseServlet extends HttpServlet {
|
||||
private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
resp.setContentType("text/html");
|
||||
PrintWriter pw = resp.getWriter();
|
||||
Principal principal = req.getUserPrincipal();
|
||||
Assert.assertNotNull(principal);
|
||||
pw.printf("<html><head><title>%s</title></head><body>", "Customer Portal");
|
||||
pw.println("Stian Thorgersen");
|
||||
pw.println("Bill Burke");
|
||||
pw.print("</body></html>");
|
||||
pw.flush();
|
||||
|
||||
|
||||
}
|
||||
}
|
82
testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java
Executable file
82
testsuite/stress/src/test/java/org/keycloak/test/FrameworkTest.java
Executable file
|
@ -0,0 +1,82 @@
|
|||
package org.keycloak.test;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.test.stress.MaxRateExecutor;
|
||||
import org.keycloak.test.stress.StressExecutor;
|
||||
import org.keycloak.test.stress.TestFactory;
|
||||
import org.keycloak.test.stress.tests.LoginLogout;
|
||||
import org.keycloak.testsuite.adapter.AdapterTestStrategy;
|
||||
import org.keycloak.testsuite.adapter.CallAuthenticatedServlet;
|
||||
import org.keycloak.testsuite.adapter.CustomerDatabaseServlet;
|
||||
import org.keycloak.testsuite.adapter.CustomerServlet;
|
||||
import org.keycloak.testsuite.adapter.InputServlet;
|
||||
import org.keycloak.testsuite.adapter.ProductServlet;
|
||||
import org.keycloak.testsuite.adapter.SessionServlet;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class FrameworkTest {
|
||||
@ClassRule
|
||||
public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
|
||||
@Override
|
||||
protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
|
||||
RealmModel realm = AdapterTestStrategy.baseAdapterTestInitialization(session, manager, adminRealm, getClass());
|
||||
|
||||
URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json");
|
||||
createApplicationDeployment()
|
||||
.name("customer-portal").contextPath("/customer-portal")
|
||||
.servletClass(org.keycloak.test.CustomerDatabaseServlet.class).adapterConfigPath(url.getPath())
|
||||
.role("user").deployApplication();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testStressExecutor() throws Exception {
|
||||
System.out.println("*************************");
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
StressExecutor executor = new StressExecutor();
|
||||
LoginLogout test = new LoginLogout();
|
||||
test.authServerUrl("http://localhost:8081/auth")
|
||||
.realm("demo")
|
||||
.username("bburke@redhat.com")
|
||||
.password("password")
|
||||
.securedResourceUrl("http://localhost:8081/customer-portal");
|
||||
test.init();
|
||||
executor.addTest(test, 5);
|
||||
long time = executor.execute();
|
||||
System.out.println("Took: " + time );
|
||||
}
|
||||
@Test
|
||||
public void testRate() throws Exception {
|
||||
System.out.println("*************************");
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
TestFactory factory = new TestFactory() {
|
||||
@Override
|
||||
public org.keycloak.test.stress.Test create() {
|
||||
LoginLogout test = new LoginLogout();
|
||||
test.authServerUrl("http://localhost:8081/auth")
|
||||
.realm("demo")
|
||||
.username("bburke@redhat.com")
|
||||
.password("password")
|
||||
.securedResourceUrl("http://localhost:8081/customer-portal");
|
||||
return test;
|
||||
}
|
||||
};
|
||||
MaxRateExecutor executor = new MaxRateExecutor();
|
||||
executor.best(factory, 4);
|
||||
executor.printResults();
|
||||
}
|
||||
|
||||
}
|
11
testsuite/stress/src/test/resources/adapter-test/cust-app-keycloak.json
Executable file
11
testsuite/stress/src/test/resources/adapter-test/cust-app-keycloak.json
Executable file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"realm": "demo",
|
||||
"resource": "customer-portal",
|
||||
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url": "http://localhost:8081/auth",
|
||||
"ssl-required" : "external",
|
||||
"expose-token": true,
|
||||
"credentials": {
|
||||
"secret": "password"
|
||||
}
|
||||
}
|
185
testsuite/stress/src/test/resources/testrealm.json
Executable file
185
testsuite/stress/src/test/resources/testrealm.json
Executable file
|
@ -0,0 +1,185 @@
|
|||
{
|
||||
"id": "test",
|
||||
"realm": "test",
|
||||
"enabled": true,
|
||||
"sslRequired": "external",
|
||||
"registrationAllowed": true,
|
||||
"resetPasswordAllowed": true,
|
||||
"editUsernameAllowed" : true,
|
||||
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
|
||||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"requiredCredentials": [ "password" ],
|
||||
"defaultRoles": [ "user" ],
|
||||
"smtpServer": {
|
||||
"from": "auto@keycloak.org",
|
||||
"host": "localhost",
|
||||
"port":"3025"
|
||||
},
|
||||
"users" : [
|
||||
{
|
||||
"username" : "test-user@localhost",
|
||||
"enabled": true,
|
||||
"email" : "test-user@localhost",
|
||||
"firstName": "Tom",
|
||||
"lastName": "Brady",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": ["user", "offline_access"],
|
||||
"clientRoles": {
|
||||
"test-app": [ "customer-user" ],
|
||||
"account": [ "view-profile", "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "john-doh@localhost",
|
||||
"enabled": true,
|
||||
"email" : "john-doh@localhost",
|
||||
"firstName": "John",
|
||||
"lastName": "Doh",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": ["user"],
|
||||
"clientRoles": {
|
||||
"test-app": [ "customer-user" ],
|
||||
"account": [ "view-profile", "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "keycloak-user@localhost",
|
||||
"enabled": true,
|
||||
"email" : "keycloak-user@localhost",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": ["user"],
|
||||
"clientRoles": {
|
||||
"test-app": [ "customer-user" ],
|
||||
"account": [ "view-profile", "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "topGroupUser",
|
||||
"enabled": true,
|
||||
"email" : "top@redhat.com",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"groups": [
|
||||
"/topGroup"
|
||||
]
|
||||
},
|
||||
{
|
||||
"username" : "level2GroupUser",
|
||||
"enabled": true,
|
||||
"email" : "level2@redhat.com",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"groups": [
|
||||
"/topGroup/level2group"
|
||||
]
|
||||
}
|
||||
],
|
||||
"scopeMappings": [
|
||||
{
|
||||
"client": "third-party",
|
||||
"roles": ["user"]
|
||||
},
|
||||
{
|
||||
"client": "test-app",
|
||||
"roles": ["user"]
|
||||
}
|
||||
],
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "test-app",
|
||||
"enabled": true,
|
||||
"baseUrl": "http://localhost:8081/app",
|
||||
"redirectUris": [
|
||||
"http://localhost:8081/app/*"
|
||||
],
|
||||
"adminUrl": "http://localhost:8081/app/logout",
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId" : "third-party",
|
||||
"enabled": true,
|
||||
"consentRequired": true,
|
||||
|
||||
"redirectUris": [
|
||||
"http://localhost:8081/app/*"
|
||||
],
|
||||
"secret": "password"
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
"realm" : [
|
||||
{
|
||||
"name": "user",
|
||||
"description": "Have User privileges"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"description": "Have Administrator privileges"
|
||||
}
|
||||
],
|
||||
"client" : {
|
||||
"test-app" : [
|
||||
{
|
||||
"name": "customer-user",
|
||||
"description": "Have Customer User privileges"
|
||||
},
|
||||
{
|
||||
"name": "customer-admin",
|
||||
"description": "Have Customer Admin privileges"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
},
|
||||
"groups" : [
|
||||
{
|
||||
"name": "topGroup",
|
||||
"attributes": {
|
||||
"topAttribute": ["true"]
|
||||
|
||||
},
|
||||
"realmRoles": ["user"],
|
||||
|
||||
"subGroups": [
|
||||
{
|
||||
"name": "level2group",
|
||||
"realmRoles": ["admin"],
|
||||
"clientRoles": {
|
||||
"test-app": ["customer-user"]
|
||||
},
|
||||
"attributes": {
|
||||
"level2Attribute": ["true"]
|
||||
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
"clientScopeMappings": {
|
||||
"test-app": [
|
||||
{
|
||||
"client": "third-party",
|
||||
"roles": ["customer-user"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"internationalizationEnabled": true,
|
||||
"supportedLocales": ["en", "de"],
|
||||
"defaultLocale": "en"
|
||||
}
|
Loading…
Reference in a new issue