/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community 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://opensource.org/licenses/ecl2.txt
*
* 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.opencastproject.util;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.entwinemedia.fn.Fn;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Ignore
public class MultiResourceLockTest {
private AtomicInteger taskCounter;
private HashMap<Long, Double> data;
private volatile boolean multipleIdsInParallel;
private MultiResourceLock lock;
private static final Logger logger = LoggerFactory.getLogger(MultiResourceLockTest.class);
@Before
public void setUp() {
taskCounter = new AtomicInteger();
data = new HashMap<>();
multipleIdsInParallel = false;
lock = new MultiResourceLock();
}
@Test(expected = java.lang.AssertionError.class)
public void testDetectNoParallelExecution() throws Exception {
// Create tasks with just one id so no parallel execution can happen.
// This will cause the parallel execution assertion to fail.
testSynchronize(Executors.newFixedThreadPool(10), 15, 1, 100, 100);
}
@Test
public void testSynchronize() throws Exception {
testSynchronize(Executors.newFixedThreadPool(10), 20, 2, 0, 0);
testSynchronize(Executors.newFixedThreadPool(10), 5, 2, 100, 500);
testSynchronize(Executors.newFixedThreadPool(2), 20, 3, 200, 1000);
testSynchronize(Executors.newScheduledThreadPool(10), 5, 2, 100, 100);
testSynchronize(Executors.newScheduledThreadPool(3), 200, 5, 700, 1000);
}
private void testSynchronize(ExecutorService pool, int taskCount, final int idCount, final int minSleep,
final int maxSleep) throws Exception {
final Random random = new Random(System.nanoTime());
for (int i = 0; i < taskCount; i++) {
taskCounter.incrementAndGet();
final String taskName = "Task " + i;
sleep((long) (Math.random() * (maxSleep - minSleep) + minSleep));
pool.execute(new Runnable() {
@Override
public void run() {
final long id = random.nextInt(idCount);
lock.synchronize(id, task(taskName, minSleep, maxSleep));
}
});
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.DAYS);
assertEquals("Some task haven't been executed", 0, taskCounter.get());
assertEquals("Not all lock objects have been removed", 0, lock.getLockMapSize());
assertTrue("No parallel execution of threads with different IDs", multipleIdsInParallel);
}
private Fn<Long, Void> task(final String taskName, final long minSleep, final long maxSleep) {
return new Fn<Long, Void>() {
@Override
public Void apply(Long id) {
final Double value = Math.random();
data.put(id, value);
final Map<Long, Double> dataCopy = new HashMap<>(data);
sleep((long) (Math.random() * (maxSleep - minSleep) + minSleep));
long seconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime());
logger.info(format("%s, id: %s, time %s", taskName, id, seconds));
assertEquals("Concurrent modification of data for id " + id, value, data.get(id));
taskCounter.decrementAndGet();
multipleIdsInParallel = multipleIdsInParallel || !dataCopy.equals(data);
return null;
}
};
}
private static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}