diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/OutdatedTopologyExceptionReproducerTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/OutdatedTopologyExceptionReproducerTest.java new file mode 100644 index 0000000000..9802934d1a --- /dev/null +++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/OutdatedTopologyExceptionReproducerTest.java @@ -0,0 +1,166 @@ +/* + * 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.sessions.infinispan.initializer; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import org.infinispan.Cache; +import org.infinispan.configuration.cache.CacheMode; +import org.infinispan.configuration.cache.Configuration; +import org.infinispan.configuration.cache.ConfigurationBuilder; +import org.infinispan.configuration.global.GlobalConfigurationBuilder; +import org.infinispan.interceptors.locking.NonTransactionalLockingInterceptor; +import org.infinispan.manager.DefaultCacheManager; +import org.infinispan.manager.EmbeddedCacheManager; +import org.infinispan.statetransfer.StateTransferInterceptor; +import org.jboss.logging.Logger; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.keycloak.connections.infinispan.InfinispanConnectionProvider; + +/** + * Reproducer for KEYCLOAK-3306. Uncomment the snippet for adding StateTransferInterceptor to have test fixed. + * + * @author Marek Posolda + */ +@Ignore +public class OutdatedTopologyExceptionReproducerTest { + + protected static final Logger logger = Logger.getLogger(OutdatedTopologyExceptionReproducerTest.class); + + private static final int THREADS_COUNT = 20; + + @Test + public void testListener() throws Exception { + EmbeddedCacheManager node1 = null; + EmbeddedCacheManager node2 = null; + + try { + node1 = createManager(); + Cache node1Cache = node1.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME); + logger.info("Node1Cache started"); + + List cacheOpsList = new ArrayList<>(); + AtomicReference exceptionHolder = new AtomicReference<>(); + + for (int i=0 ; i node2Cache = node2.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME); + logger.info("Node2Cache started"); + + for (CacheOperations cacheOps : cacheOpsList) { + cacheOps.stopMe(); + } + for (CacheOperations cacheOps : cacheOpsList) { + cacheOps.join(); + } + + logger.info("All CacheOperations threads stopped"); + + Exception ex = exceptionHolder.get(); + if (ex != null) { + Assert.fail("Some exception was thrown. It was: " + ex.getClass().getName() + ": " + ex.getMessage()); + } + + logger.info("Test finished successfuly"); + } finally { + node2.stop(); + node1.stop(); + } + } + + private EmbeddedCacheManager createManager() { + System.setProperty("java.net.preferIPv4Stack", "true"); + System.setProperty("jgroups.tcp.port", "53715"); + GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder(); + + boolean clustered = true; + boolean async = false; + boolean allowDuplicateJMXDomains = true; + + if (clustered) { + gcb = gcb.clusteredDefault(); + gcb.transport().clusterName("test-clustering"); + } + + gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains); + + EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build()); + + + ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder(); + if (clustered) { + invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC); + } + + // Uncomment this to have test fixed!!! +// invalidationConfigBuilder.customInterceptors() +// .addInterceptor() +// .before(NonTransactionalLockingInterceptor.class) +// .interceptorClass(StateTransferInterceptor.class); + + Configuration invalidationCacheConfiguration = invalidationConfigBuilder.build(); + + cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationCacheConfiguration); + return cacheManager; + + } + + private class CacheOperations extends Thread { + + private final Cache cache; + private final AtomicBoolean stopped = new AtomicBoolean(false); + private final AtomicReference exceptionHolder; + private final String key; + + public CacheOperations(Cache cache, String key, AtomicReference exceptionHolder) { + this.cache = cache; + this.key = key; + this.exceptionHolder = exceptionHolder; + } + + @Override + public void run() { + try { + while (!stopped.get()) { + cache.putForExternalRead(key, new Object()); + cache.remove(key); + } + } catch (Exception e) { + exceptionHolder.set(e); + throw e; + } + } + + public void stopMe() { + stopped.set(true); + } + + } +}