Search in sources :

Example 6 with ContainerException

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
}
Also used : ArrayList(java.util.ArrayList) ContainerException(net.dempsy.container.ContainerException) DempsyException(net.dempsy.DempsyException) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) LinkedList(java.util.LinkedList) KeyedMessageWithType(net.dempsy.messages.KeyedMessageWithType) ContainerException(net.dempsy.container.ContainerException) KeyedMessage(net.dempsy.messages.KeyedMessage)

Example 7 with ContainerException

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;
}
Also used : ContainerException(net.dempsy.container.ContainerException) DempsyException(net.dempsy.DempsyException)

Aggregations

ContainerException (net.dempsy.container.ContainerException)7 DempsyException (net.dempsy.DempsyException)5 KeyedMessage (net.dempsy.messages.KeyedMessage)3 RejectedExecutionException (java.util.concurrent.RejectedExecutionException)2 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)2 KeyedMessageWithType (net.dempsy.messages.KeyedMessageWithType)2 ArrayList (java.util.ArrayList)1 LinkedList (java.util.LinkedList)1 QuietCloseable (net.dempsy.util.QuietCloseable)1