use of net.dempsy.messages.KeyedMessageWithType in project Dempsy by Dempsy.
the class TestInstanceManager method testSingleInstanceTwoMessagesSameClassCombinedExecution.
@Test
public void testSingleInstanceTwoMessagesSameClassCombinedExecution() throws Exception {
final CombinedMP prototype = new CombinedMP();
try (final LockingContainer manager = setupContainer(new MessageProcessor<CombinedMP>(prototype))) {
final DummyDispatcher dispatcher = ((DummyDispatcher) manager.getDispatcher());
assertEquals("starts with no instances", 0, manager.getProcessorCount());
final KeyedMessageWithType message1 = km(new MessageOne(123));
final InstanceWrapper wrapper = manager.getInstanceForKey(message1.key, message1.message);
manager.dispatch(message1, Operation.handle, true);
assertEquals("instance was created", 1, manager.getProcessorCount());
final KeyedMessageWithType message2 = km(new MessageOne(123));
assertSame("same wrapper returned for second message", wrapper, manager.getInstanceForKey(message2.key, message2.message));
manager.dispatch(message2, Operation.handle, true);
final CombinedMP instance = (CombinedMP) wrapper.getInstance();
assertEquals("no other instance was created", 1, manager.getProcessorCount());
assertEquals("instance activated", 1, instance.activationCount);
assertTrue("real activation time", instance.activationTime > 0);
assertTrue("activated before first message", instance.activationTime < instance.firstMessageTime);
assertEquals("both messages delivered", 2, instance.messages.size());
assertSame("message1 delivered first", message1.message, instance.messages.get(0));
assertSame("message2 delivered second", message2.message, instance.messages.get(1));
assertEquals(new ReturnString("MessageOne"), dispatcher.lastDispatched.message);
}
}
use of net.dempsy.messages.KeyedMessageWithType in project Dempsy by Dempsy.
the class TestInstanceManager method testQueueIsClearedAfterExecution.
// This test no longer really matters since there is no queue but we might as well leave it
// since it exercises the container.
@Test
public void testQueueIsClearedAfterExecution() throws Exception {
final CombinedMP prototype = new CombinedMP();
try (final LockingContainer manager = setupContainer(new MessageProcessor<CombinedMP>(prototype))) {
final KeyedMessageWithType message = km(new MessageOne(123));
final InstanceWrapper wrapper = manager.getInstanceForKey(message.key, message.message);
manager.dispatch(message, Operation.handle, true);
assertEquals("instance was created", 1, manager.getProcessorCount());
final CombinedMP instance = (CombinedMP) wrapper.getInstance();
assertEquals("instance activated", 1, instance.activationCount);
assertTrue("real activation time", instance.activationTime > 0);
assertSame("instance received message", message.message, instance.messages.get(0));
assertEquals("message count", 1, instance.messages.size());
assertTrue("activated before first message", instance.activationTime < instance.firstMessageTime);
final long activationTime = instance.activationTime;
final long firstMessageTime = instance.firstMessageTime;
// here is where the queue would have been advanced again ... but there is no queue anymore.
assertTrue("activation time didn't change", activationTime == instance.activationTime);
assertTrue("message time didn't change", firstMessageTime == instance.firstMessageTime);
assertEquals("message count didn't change", 1, instance.messages.size());
}
}
use of net.dempsy.messages.KeyedMessageWithType in project Dempsy by Dempsy.
the class TestInstanceManager method testOutput.
@Test
public void testOutput() throws Exception {
final OutputTestMP prototype = new OutputTestMP();
try (final LockingContainer manager = setupContainer(new MessageProcessor<OutputTestMP>(prototype))) {
final DummyDispatcher dispatcher = ((DummyDispatcher) manager.getDispatcher());
// we need to dispatch messages to create MP instances
final KeyedMessageWithType message1 = km(new MessageOne(1));
final InstanceWrapper wrapper1 = manager.getInstanceForKey(message1.key, message1.message);
manager.dispatch(message1, Operation.handle, true);
final KeyedMessageWithType message2 = km(new MessageOne(2));
final InstanceWrapper wrapper2 = manager.getInstanceForKey(message2.key, message2.message);
manager.dispatch(message2, Operation.handle, true);
assertEquals(new ReturnString("MessageOne"), dispatcher.lastDispatched.message);
manager.outputPass();
final OutputTestMP mp1 = (OutputTestMP) wrapper1.getInstance();
assertTrue("MP1 output did not occur after activation", mp1.activationTime < mp1.outputTime);
final OutputTestMP mp2 = (OutputTestMP) wrapper2.getInstance();
assertTrue("MP2 output did not occur after activation", mp2.activationTime < mp2.outputTime);
assertTrue(mp1 != mp2);
assertEquals(new ReturnInt(42), dispatcher.lastDispatched.message);
}
}
use of net.dempsy.messages.KeyedMessageWithType in project Dempsy by Dempsy.
the class LockingContainer method dispatch.
// this is called directly from tests but shouldn't be accessed otherwise.
@Override
public void dispatch(final KeyedMessage message, final boolean block) throws IllegalArgumentException, ContainerException {
if (!isRunningLazy) {
LOGGER.debug("Dispacth called on stopped container");
statCollector.messageFailed(false);
}
if (message == null)
// No. We didn't process the null message
return;
if (!inbound.doesMessageKeyBelongToNode(message.key)) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Message with key " + SafeString.objectDescription(message.key) + " sent to wrong container. ");
statCollector.messageFailed(false);
return;
}
boolean evictedAndBlocking;
if (message == null || message.message == null)
throw new IllegalArgumentException("the container for " + clusterId + " attempted to dispatch null message.");
if (message.key == null)
throw new ContainerException("Message " + objectDescription(message.message) + " contains no key.");
try {
numBeingWorked.incrementAndGet();
do {
evictedAndBlocking = false;
final InstanceWrapper wrapper = getInstanceForKey(message.key);
// wrapper will be null if the activate returns 'false'
if (wrapper != null) {
final Object instance = wrapper.getExclusive(block);
if (instance != null) {
// null indicates we didn't get the lock
final List<KeyedMessageWithType> response;
try {
if (wrapper.isEvicted()) {
response = null;
// a subsequent call to getInstanceForDispatch will create a new one.
if (block) {
Thread.yield();
// we're going to try again.
evictedAndBlocking = true;
} else {
// otherwise it's just like we couldn't get the lock. The Mp is busy being killed off.
if (LOGGER.isTraceEnabled())
LOGGER.trace("the container for " + clusterId + " failed handle message due to evicted Mp " + SafeString.valueOf(prototype));
statCollector.messageCollision(message);
}
} else {
response = invokeOperation(wrapper.getInstance(), Operation.handle, message);
}
} finally {
wrapper.releaseLock();
}
if (response != null) {
try {
dispatcher.dispatch(response);
} catch (final Exception de) {
if (isRunning.get())
LOGGER.warn("Failed on subsequent dispatch of " + response + ": " + de.getLocalizedMessage());
}
}
} else {
// ... we didn't get the lock
if (LOGGER.isTraceEnabled())
LOGGER.trace("the container for " + clusterId + " failed to obtain lock on " + SafeString.valueOf(prototype));
statCollector.messageCollision(message);
}
} else {
// if we got here then the activate on the Mp explicitly returned 'false'
if (LOGGER.isDebugEnabled())
LOGGER.debug("the container for " + clusterId + " failed to activate the Mp for " + SafeString.valueOf(prototype));
// leave the do/while loop
break;
}
} while (evictedAndBlocking);
} finally {
numBeingWorked.decrementAndGet();
}
}
use of net.dempsy.messages.KeyedMessageWithType in project Dempsy by Dempsy.
the class NonLockingContainer method outputPass.
// TODO: Output concurrency blocks normal message handling. Need a means of managing this better.
// This method MUST NOT THROW
@Override
protected void outputPass() {
if (!prototype.isOutputSupported())
return;
// take a snapshot of the current container state.
final LinkedList<Object> toOutput = new LinkedList<Object>(instances.keySet());
Executor executorService = null;
Semaphore taskLock = null;
executorService = super.getOutputExecutorService();
if (executorService != null)
taskLock = new Semaphore(outputConcurrency);
// This keeps track of the number of concurrently running
// output tasks so that this method can wait until they're
// all done to return.
//
// It's also used as a condition variable signaling on its
// own state changes.
final AtomicLong numExecutingOutputs = new AtomicLong(0);
// keep going until all of the outputs have been invoked
while (toOutput.size() > 0 && isRunning.get()) {
for (final Iterator<Object> iter = toOutput.iterator(); iter.hasNext(); ) {
final Object key = iter.next();
final WorkingPlaceholder wp = new WorkingPlaceholder();
// we're going to hold up all incomming message to this mp
// this blocks other threads from
wp.mailbox.getAndSet(null);
// dropping messages in the mailbox
// try to get a lock
final WorkingPlaceholder alreadyThere = working.putIfAbsent(key, wp);
if (alreadyThere == null) {
// we got it the lock
final Object instance = instances.get(key);
if (instance != null) {
final Semaphore taskSepaphore = taskLock;
// This task will release the wrapper's lock.
final Runnable task = new Runnable() {
@Override
public void run() {
final List<KeyedMessageWithType> response;
try {
if (isRunning.get())
response = invokeOperation(instance, Operation.output, null);
else
response = null;
} finally {
// releases this back to the world
working.remove(key);
// this signals that we're done.
synchronized (numExecutingOutputs) {
numExecutingOutputs.decrementAndGet();
numExecutingOutputs.notifyAll();
}
if (taskSepaphore != null)
taskSepaphore.release();
}
if (response != null) {
try {
dispatcher.dispatch(response);
} catch (final Exception de) {
if (isRunning.get())
LOGGER.warn("Failed on subsequent dispatch of " + response + ": " + de.getLocalizedMessage());
}
}
}
};
synchronized (numExecutingOutputs) {
numExecutingOutputs.incrementAndGet();
}
if (executorService != null) {
try {
taskSepaphore.acquire();
executorService.execute(task);
} catch (final RejectedExecutionException e) {
// we never got into the run so we need to release the lock
working.remove(key);
// this may happen because of a race condition between the
taskSepaphore.release();
} catch (final InterruptedException e) {
// this can happen while blocked in the semaphore.acquire.
// if we're no longer running we should just get out
// of here.
//
// Not releasing the taskSepaphore assumes the acquire never executed.
// if (since) the acquire never executed we also need to release the
// wrapper lock or that Mp will never be usable again.
// we never got into the run so we need to release the lock
working.remove(key);
}
} else
task.run();
iter.remove();
} else {
working.remove(key);
LOGGER.warn("There was an attempt to evict a non-existent Mp for key " + SafeString.objectDescription(key));
}
}
// didn't get the lock
}
// loop over every mp
}
// now make sure all of the running tasks have completed
synchronized (numExecutingOutputs) {
while (numExecutingOutputs.get() > 0) {
try {
numExecutingOutputs.wait();
} catch (final InterruptedException e) {
// waiting for all of the threads to finish
if (!isRunning.get())
break;
// otherwise continue checking.
}
}
}
// =======================================================
}
Aggregations