/**
*
*/
package vnet.routing.netty.server.support.window;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import vnet.routing.netty.server.support.window.spi.UnreleasedMessagesHandler;
/**
* @author obergner
*
*/
public class WindowStoreConcurrentTest {
/**
* Test method for
* {@link vnet.routing.netty.server.support.window.WindowStore#storeMessage(long, java.io.Serializable)}
* .
*
* @throws InterruptedException
* @throws ExecutionException
*/
@Test
public final void assertThatStoreMessageFillsWindowStoreToCapacity()
throws InterruptedException, ExecutionException {
final int capacity = 10000;
final BlockingQueue<IdPlusMessage> queue = new ArrayBlockingQueue<IdPlusMessage>(
capacity);
final Set<IdPlusMessage> backup = new HashSet<IdPlusMessage>(capacity);
for (long key = 0; key < capacity; key++) {
final IdPlusMessage idPlusMessage = new IdPlusMessage(key,
String.valueOf(key));
queue.put(idPlusMessage);
backup.add(idPlusMessage);
}
final WindowStore objectUnderTest = new WindowStore(
"assertThatStoreMessageFillsWindowStoreToCapacity", capacity,
new LoggingUnreleaseMessagesHandler());
final int numberOfTasks = 100;
final Set<MessageStoreTask> messageStoreTasks = new HashSet<MessageStoreTask>(
numberOfTasks);
for (int i = 0; i < numberOfTasks; i++) {
messageStoreTasks.add(new MessageStoreTask(objectUnderTest, queue));
}
final ExecutorService executor = Executors
.newFixedThreadPool(numberOfTasks);
final Set<Future<?>> completions = new HashSet<Future<?>>();
for (final MessageStoreTask messageStoreTask : messageStoreTasks) {
completions.add(executor.submit(messageStoreTask));
}
for (final Future<?> completion : completions) {
completion.get();
}
executor.shutdown();
executor.awaitTermination(3, TimeUnit.SECONDS);
executor.shutdownNow();
assertEquals("WindowStore should be completely filled, yet it isn't",
capacity, objectUnderTest.getCurrentMessageCount());
for (final IdPlusMessage idPlusMessage : backup) {
final String storedMessage = (String) objectUnderTest
.releaseMessage(idPlusMessage.id);
assertNotNull("WindowStore should contain id " + idPlusMessage.id
+ ", yet it doesn't", storedMessage);
assertEquals("WindowStore stored wrong message under id "
+ idPlusMessage.id, idPlusMessage.message, storedMessage);
}
}
private static class IdPlusMessage {
final long id;
final String message;
public IdPlusMessage(final long id, final String message) {
this.id = id;
this.message = message;
}
}
private static class LoggingUnreleaseMessagesHandler implements
UnreleasedMessagesHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public void onShutDown(final Map<Long, Serializable> unreleasedMessages) {
this.log.info("Unreleased messages: {}", unreleasedMessages);
}
}
private static class MessageStoreTask implements Runnable {
private final WindowStore objectUnderTest;
private final BlockingQueue<IdPlusMessage> queue;
public MessageStoreTask(final WindowStore objectUnderTest,
final BlockingQueue<IdPlusMessage> queue) {
this.objectUnderTest = objectUnderTest;
this.queue = queue;
}
@Override
public void run() {
while (true) {
final IdPlusMessage next = this.queue.poll();
if (next == null) {
break;
}
final boolean success = this.objectUnderTest.storeMessage(
next.id, next.message);
if (!success) {
throw new IllegalStateException(
"Failed to store message [id = " + next.id
+ "|message = " + next.message + "]");
}
}
}
}
/**
* Test method for
* {@link vnet.routing.netty.server.support.window.WindowStore#storeMessage(long, java.io.Serializable)}
* .
*
* @throws Throwable
*/
@Test(expected = IllegalStateException.class)
public final void assertThatStoreMessageRefusesToFillWindowStoreBeyondCapacity()
throws Throwable {
ExecutorService executor = null;
try {
final int capacity = 10000;
final int numberOfMessagesToStore = capacity + 1;
final BlockingQueue<IdPlusMessage> queue = new ArrayBlockingQueue<IdPlusMessage>(
numberOfMessagesToStore);
for (long key = 0; key < numberOfMessagesToStore; key++) {
final IdPlusMessage idPlusMessage = new IdPlusMessage(key,
String.valueOf(key));
queue.put(idPlusMessage);
}
final WindowStore objectUnderTest = new WindowStore(
"assertThatStoreMessageFillsWindowStoreToCapacity",
capacity, new LoggingUnreleaseMessagesHandler());
final int numberOfTasks = 100;
final Set<MessageStoreTask> messageStoreTasks = new HashSet<MessageStoreTask>(
numberOfTasks);
for (int i = 0; i < numberOfTasks; i++) {
messageStoreTasks.add(new MessageStoreTask(objectUnderTest,
queue));
}
executor = Executors.newFixedThreadPool(numberOfTasks);
final Set<Future<?>> completions = new HashSet<Future<?>>();
for (final MessageStoreTask messageStoreTask : messageStoreTasks) {
completions.add(executor.submit(messageStoreTask));
}
for (final Future<?> completion : completions) {
completion.get();
}
} catch (final ExecutionException e) {
throw e.getCause();
} finally {
if (executor != null) {
executor.shutdown();
executor.awaitTermination(3, TimeUnit.SECONDS);
executor.shutdownNow();
}
}
}
}