use of net.dempsy.container.ContainerException in project Dempsy by Dempsy.
the class NonLockingContainer method dispatch.
@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 (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.");
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;
}
final Object key = message.key;
boolean keepTrying = true;
while (keepTrying) {
final MutRef<WorkingPlaceholder> wph = new MutRef<>();
final WorkingPlaceholder alreadyThere = putIfAbsent(working, key, () -> wph.set(new WorkingPlaceholder()));
if (alreadyThere == null) {
// we're it!
final WorkingPlaceholder wp = wph.ref;
// we're not going to keep trying.
keepTrying = false;
// these will be dispatched while NOT having the lock
List<KeyedMessageWithType> response = null;
try {
// if we don't get the WorkingPlaceholder out of the working map then that Mp will forever be lost.
// we're working one.
numBeingWorked.incrementAndGet();
Object instance = instances.get(key);
if (instance == null) {
try {
// this can throw
instance = createAndActivate(key);
} catch (final RuntimeException e) {
// container or runtime exception
// This will drain the swamp
LOGGER.debug("Failed to process message with key " + SafeString.objectDescription(message.key), e);
instance = null;
}
}
if (instance == null) {
// activation or creation failed.
// decrement for this one
numBeingWorked.decrementAndGet();
LOGGER.debug("Can't handle message {} because the creation of the Mp seems to have failed.", SafeString.objectDescription(key));
final WorkingQueueHolder mailbox = getQueue(wp);
if (mailbox.queue != null) {
mailbox.queue.forEach(m -> {
LOGGER.debug("Failed to process message with key " + SafeString.objectDescription(m.key));
statCollector.messageFailed(true);
// decrement for each in the queue
numBeingWorked.decrementAndGet();
});
}
} else {
KeyedMessage curMessage = message;
while (curMessage != null) {
// can't be null the first time
final List<KeyedMessageWithType> resp = invokeOperation(instance, Operation.handle, curMessage);
if (resp != null) {
// these responses will be dispatched after we release the lock.
if (response == null)
response = new ArrayList<>();
response.addAll(resp);
}
// decrement the initial increment.
numBeingWorked.decrementAndGet();
// work off the queue.
// spin until I have it.
final WorkingQueueHolder mailbox = getQueue(wp);
if (mailbox.queue != null && mailbox.queue.size() > 0) {
// if there are messages in the queue
// take a message off the queue
curMessage = mailbox.queue.removeFirst();
// curMessage CAN'T be NULL!!!!
// releasing the lock on the mailbox ... we're ready to process 'curMessage' on the next loop
wp.mailbox.set(mailbox);
} else {
curMessage = null;
// (1) NOTE: DON'T put the queue back. This will prevent ALL other threads trying to drop a message
// in this box. When an alternate thread tries to open the mailbox to put a message in, if it can't,
// because THIS thread's left it locked, the other thread starts the process from the beginning
// re-attempting to get exclusive control over the Mp. In other words, the other thread only makes
// a single attempt and if it fails it goes back to attempting to get the Mp from the beginning.
//
// This thread cannot give up the current Mp if there's a potential for any data to end up in the
// queue. Since we're about to give up the Mp we cannot allow the mailbox to become available
// therefore we cannot allow any other threads to spin on it.
}
}
}
} finally {
if (working.remove(key) == null)
LOGGER.error("IMPOSSIBLE! Null key removed from working set.", new RuntimeException());
}
if (response != null) {
try {
dispatcher.dispatch(response);
} catch (final Exception de) {
LOGGER.warn("Failed on subsequent dispatch of " + response + ": " + de.getLocalizedMessage());
}
}
} else {
// ... we didn't get the lock
if (!block) {
// blocking means no collisions allowed.
if (LOGGER.isTraceEnabled())
LOGGER.trace("the container for " + clusterId + " failed to obtain lock on " + SafeString.valueOf(prototype));
statCollector.messageCollision(message);
keepTrying = false;
} else {
// try and get the queue.
final WorkingQueueHolder mailbox = alreadyThere.mailbox.getAndSet(null);
if (mailbox != null) {
// we got the queue!
try {
keepTrying = false;
// drop a message in the mailbox queue and mark it as being worked.
numBeingWorked.incrementAndGet();
if (mailbox.queue == null)
mailbox.queue = new LinkedList<>();
mailbox.queue.add(message);
} finally {
// put it back - releasing the lock
alreadyThere.mailbox.set(mailbox);
}
} else {
// if we didn't get the queue, we need to start completely over.
// otherwise there's a potential race condition - see the note at (1).
// we failed to get the queue ... maybe we'll have better luck next time.
}
}
// we didn't get the lock and we're blocking and we're now done handling the mailbox
}
// we didn't get the lock so we tried the mailbox (or ended becasuse we're non-blocking)
}
// keep working
}
use of net.dempsy.container.ContainerException in project Dempsy by Dempsy.
the class NonLockingContainer method createAndActivate.
// ----------------------------------------------------------------------------
// Test Hooks
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// Internals
// ----------------------------------------------------------------------------
// this is called directly from tests but shouldn't be accessed otherwise.
private Object createAndActivate(final Object key) throws ContainerException {
Object instance = null;
try {
instance = prototype.newInstance();
} catch (final DempsyException e) {
if (e.userCaused()) {
LOGGER.warn("The message processor prototype " + SafeString.valueOf(prototype) + " threw an exception when trying to create a new message processor for they key " + SafeString.objectDescription(key));
statCollector.messageFailed(true);
instance = null;
} else
throw new ContainerException("the container for " + clusterId + " failed to create a new instance of " + SafeString.valueOf(prototype) + " for the key " + SafeString.objectDescription(key) + " because the clone method threw an exception.", e);
} catch (final RuntimeException e) {
throw new ContainerException("the container for " + clusterId + " failed to create a new instance of " + SafeString.valueOf(prototype) + " for the key " + SafeString.objectDescription(key) + " because the clone invocation resulted in an unknown exception.", e);
}
// activate
boolean activateSuccessful = false;
try {
if (instance != null) {
if (LOGGER.isTraceEnabled())
LOGGER.trace("the container for " + clusterId + " is activating instance " + String.valueOf(instance) + " via " + SafeString.valueOf(prototype));
prototype.activate(instance, key);
activateSuccessful = true;
}
} catch (final DempsyException e) {
if (e.userCaused()) {
LOGGER.warn("The message processor " + SafeString.objectDescription(instance) + " activate call threw an exception.");
statCollector.messageFailed(true);
instance = null;
} else
throw new ContainerException("the container for " + clusterId + " failed to invoke the activate method of " + SafeString.valueOf(prototype) + ". Is the active method accessible - the class is public and the method is public?", e);
} catch (final RuntimeException e) {
throw new ContainerException("the container for " + clusterId + " failed to invoke the activate method of " + SafeString.valueOf(prototype) + " because of an unknown exception.", e);
}
if (activateSuccessful) {
// must have been successful.
if (// once it goes into the map, we can remove it from the 'being worked' set
instances.putIfAbsent(key, instance) != null)
throw new IllegalStateException("WTF?");
// the newly added one.
statCollector.messageProcessorCreated(key);
}
return instance;
}
Aggregations