/* * Copyright Terracotta, Inc. * * 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.ehcache.clustered.client; import org.ehcache.PersistentCacheManager; import org.ehcache.clustered.client.config.ClusteredStoreConfiguration; import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder; import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder; import org.ehcache.clustered.client.internal.UnitTestConnectionService; import org.ehcache.clustered.common.Consistency; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.MemoryUnit; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; /** * This test makes sure a clustered cache can be opened from many client instances. As usual with concurrency tests, a * success doesn't mean it will work forever and a failure might not occur reliably. However, it puts together all * conditions to make it fail in case of race condition * * @author Henri Tremblay */ public class ClusteredConcurrencyTest { private static final URI CLUSTER_URI = URI.create("terracotta://example.com:9540/my-application"); private static final String CACHE_NAME = "clustered-cache"; private AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); @Before public void definePassthroughServer() throws Exception { UnitTestConnectionService.add(CLUSTER_URI, new UnitTestConnectionService.PassthroughServerBuilder() .resource("primary-server-resource", 64, MemoryUnit.MB) .resource("secondary-server-resource", 64, MemoryUnit.MB) .build()); } @After public void removePassthroughServer() throws Exception { UnitTestConnectionService.remove(CLUSTER_URI); } @Test public void test() throws Throwable { final int THREAD_NUM = 50; final CountDownLatch latch = new CountDownLatch(THREAD_NUM + 1); List<Thread> threads = new ArrayList<Thread>(THREAD_NUM); for (int i = 0; i < THREAD_NUM; i++) { Thread t1 = new Thread(content(latch)); t1.start(); threads.add(t1); } latch.countDown(); latch.await(); for(Thread t : threads) { t.join(); } Throwable throwable = exception.get(); if(throwable != null) { throw throwable; } } private Runnable content(final CountDownLatch latch) { return new Runnable() { @Override public void run() { try { CacheManagerBuilder<PersistentCacheManager> clusteredCacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder() .with(ClusteringServiceConfigurationBuilder.cluster(CLUSTER_URI).autoCreate() .defaultServerResource("primary-server-resource") .resourcePool("resource-pool-a", 32, MemoryUnit.MB) .resourcePool("resource-pool-b", 32, MemoryUnit.MB, "secondary-server-resource")) .withCache(CACHE_NAME, CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .with(ClusteredResourcePoolBuilder.clusteredDedicated("primary-server-resource", 32, MemoryUnit.MB))) .add(new ClusteredStoreConfiguration(Consistency.STRONG))); latch.countDown(); try { latch.await(); } catch (InterruptedException e) { // continue } clusteredCacheManagerBuilder.build(true); } catch (Throwable t) { exception.compareAndSet(null, t); // only keep the first exception } } }; } }