Package org.mule.routing

Source Code of org.mule.routing.AsynchronousUntilSuccessfulProcessingStrategy

/*
* Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.routing;

import static org.mule.routing.UntilSuccessful.DEFAULT_PROCESS_ATTEMPT_COUNT_PROPERTY_VALUE;
import static org.mule.routing.UntilSuccessful.PROCESS_ATTEMPT_COUNT_PROPERTY_NAME;

import org.mule.DefaultMuleEvent;
import org.mule.DefaultMuleMessage;
import org.mule.VoidMuleEvent;
import org.mule.api.MessagingException;
import org.mule.api.MuleEvent;
import org.mule.api.MuleMessage;
import org.mule.api.exception.MessagingExceptionHandler;
import org.mule.api.exception.MessagingExceptionHandlerAware;
import org.mule.api.lifecycle.Initialisable;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.lifecycle.Startable;
import org.mule.api.lifecycle.Stoppable;
import org.mule.api.store.ObjectStoreException;
import org.mule.config.i18n.CoreMessages;
import org.mule.config.i18n.MessageFactory;
import org.mule.retry.RetryPolicyExhaustedException;
import org.mule.util.concurrent.ThreadNameHelper;
import org.mule.util.queue.objectstore.QueueKey;
import org.mule.util.store.QueuePersistenceObjectStore;

import java.io.NotSerializableException;
import java.io.Serializable;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Until successful asynchronous processing strategy.
* <p/>
* It will return successfully to the flow executing the router once it was able to
* store the message in the object store.
* <p/>
* After that it will asynchronously try to process the message through the internal route.
* If route was not successfully executed after the configured retry count then the message
* will be routed to the defined dead letter queue route or in case there is no dead letter
* queue route then it will be handled by the flow exception strategy.
*/
public class AsynchronousUntilSuccessfulProcessingStrategy extends AbstractUntilSuccessfulProcessingStrategy implements Initialisable, Startable, Stoppable, MessagingExceptionHandlerAware
{

    protected transient Log logger = LogFactory.getLog(getClass());
    private MessagingExceptionHandler messagingExceptionHandler;
    private ScheduledExecutorService scheduledPool;

    @Override
    public void initialise() throws InitialisationException
    {
        if (getUntilSuccessfulConfiguration().getObjectStore() == null)
        {
            throw new InitialisationException(
                    MessageFactory.createStaticMessage("A ListableObjectStore must be configured on UntilSuccessful."),
                    this);
        }
    }

    @Override
    public void start()
    {
        final String threadPrefix = String.format("%s%s.%s", ThreadNameHelper.getPrefix(getUntilSuccessfulConfiguration().getMuleContext()),
                                                  getUntilSuccessfulConfiguration().getFlowConstruct().getName(), "until-successful");
        scheduledPool = getUntilSuccessfulConfiguration().getThreadingProfile().createScheduledPool(threadPrefix);
        scheduleAllPendingEventsForProcessing();
    }


    @Override
    public void stop()
    {
        scheduledPool.shutdown();
        scheduledPool = null;
    }

    @Override
    public MuleEvent route(MuleEvent event) throws MessagingException
    {
        try
        {
            ensurePayloadSerializable(event);
        }
        catch (final Exception e)
        {
            throw new MessagingException(
                    MessageFactory.createStaticMessage("Failed to prepare message for processing"), event, e, getUntilSuccessfulConfiguration().getRouter());
        }
        try
        {
            final Serializable eventStoreKey = storeEvent(event);
            scheduleForProcessing(eventStoreKey, true);
            if (getUntilSuccessfulConfiguration().getAckExpression() == null)
            {
                return VoidMuleEvent.getInstance();
            }
            return processResponseThroughAckResponseExpression(event);
        }
        catch (final Exception e)
        {
            throw new MessagingException(
                    MessageFactory.createStaticMessage("Failed to schedule the event for processing"), event, e,
                    getUntilSuccessfulConfiguration().getRouter());
        }
    }

    private void scheduleAllPendingEventsForProcessing()
    {
        try
        {
            for (final Serializable eventStoreKey : getUntilSuccessfulConfiguration().getObjectStore().allKeys())
            {
                try
                {
                    scheduleForProcessing(eventStoreKey, true);
                }
                catch (final Exception e)
                {
                    logger.error(
                            MessageFactory.createStaticMessage("Failed to schedule for processing event stored with key: "
                                                               + eventStoreKey), e);
                }
            }
        }
        catch (Exception e)
        {
            logger.warn("Failure during scheduling of until successful previous jobs " + e.getMessage());
            if (logger.isDebugEnabled())
            {
                logger.debug(e);
            }
        }
    }

    private void scheduleForProcessing(final Serializable eventStoreKey, boolean firstTime) throws Exception
    {
        this.scheduledPool.schedule(new Callable<Object>()
        {
            @Override
            public Object call() throws Exception
            {
                try
                {
                    retrieveAndProcessEvent(eventStoreKey);
                }
                catch (Exception e)
                {
                    incrementProcessAttemptCountAndRescheduleOrRemoveFromStore(eventStoreKey);
                }
                return null;
            }
        }, firstTime ? 0 : getUntilSuccessfulConfiguration().getMillisBetweenRetries(), TimeUnit.MILLISECONDS);
    }

    private void incrementProcessAttemptCountAndRescheduleOrRemoveFromStore(final Serializable eventStoreKey) throws Exception
    {
        try
        {
            final MuleEvent event = getUntilSuccessfulConfiguration().getObjectStore().remove(eventStoreKey);
            final MuleEvent mutableEvent = threadSafeCopy(event);

            final MuleMessage message = mutableEvent.getMessage();
            final Integer deliveryAttemptCount = message.getInvocationProperty(
                    PROCESS_ATTEMPT_COUNT_PROPERTY_NAME, DEFAULT_PROCESS_ATTEMPT_COUNT_PROPERTY_VALUE);

            if (deliveryAttemptCount <= getUntilSuccessfulConfiguration().getMaxRetries())
            {
                // we store the incremented version unless the max attempt count has
                // been reached
                message.setInvocationProperty(PROCESS_ATTEMPT_COUNT_PROPERTY_NAME, deliveryAttemptCount + 1);
                getUntilSuccessfulConfiguration().getObjectStore().store(eventStoreKey, mutableEvent);
                this.scheduleForProcessing(eventStoreKey, false);
            }
            else
            {
                abandonRetries(event, mutableEvent);
            }
        }
        catch (final ObjectStoreException ose)
        {
            logger.error("Failed to increment failure count for event stored with key: " + eventStoreKey, ose);
        }
    }

    private Serializable storeEvent(final MuleEvent event) throws ObjectStoreException
    {
        final MuleMessage message = event.getMessage();
        final Integer deliveryAttemptCount = message.getInvocationProperty(
                PROCESS_ATTEMPT_COUNT_PROPERTY_NAME, DEFAULT_PROCESS_ATTEMPT_COUNT_PROPERTY_VALUE);
        return storeEvent(event, deliveryAttemptCount);
    }

    private Serializable storeEvent(final MuleEvent event, final int deliveryAttemptCount)
            throws ObjectStoreException
    {
        final MuleMessage message = event.getMessage();
        message.setInvocationProperty(PROCESS_ATTEMPT_COUNT_PROPERTY_NAME, deliveryAttemptCount);
        final Serializable eventStoreKey = buildQueueKey(event);
        getUntilSuccessfulConfiguration().getObjectStore().store(eventStoreKey, event);
        return eventStoreKey;
    }

    public static Serializable buildQueueKey(final MuleEvent muleEvent)
    {
        // the key is built in way to prevent UntilSuccessful workers across a
        // cluster to compete for the same
        // events over a shared object store
        String key = muleEvent.getFlowConstruct() + "-" + muleEvent.getMuleContext().getClusterId() + "-"
                     + muleEvent.getId();
        return new QueueKey(QueuePersistenceObjectStore.DEFAULT_QUEUE_STORE, key);
    }

    private void abandonRetries(final MuleEvent event, final MuleEvent mutableEvent)
    {
        if (getUntilSuccessfulConfiguration().getDlqMP() == null)
        {
            logger.info("Retry attempts exhausted and no DLQ defined");
            RetryPolicyExhaustedException retryPolicyExhaustedException = new RetryPolicyExhaustedException(
                    CoreMessages.createStaticMessage("until-successful retries exhausted"), this);
            messagingExceptionHandler.handleException(new MessagingException(event, retryPolicyExhaustedException), event);
            return;
        }

        logger.info("Retry attempts exhausted, routing message to DLQ: " + getUntilSuccessfulConfiguration().getDlqMP());
        try
        {
            getUntilSuccessfulConfiguration().getDlqMP().process(mutableEvent);
        }
        catch (MessagingException e)
        {
            messagingExceptionHandler.handleException(e, event);
        }
        catch (Exception e)
        {
            messagingExceptionHandler.handleException(new MessagingException(event, e), event);
        }
    }

    private void removeFromStore(final Serializable eventStoreKey)
    {
        try
        {
            getUntilSuccessfulConfiguration().getObjectStore().remove(eventStoreKey);
        }
        catch (final ObjectStoreException ose)
        {
            logger.warn("Failed to remove following event from store with key: " + eventStoreKey);
        }
    }

    private void retrieveAndProcessEvent(final Serializable eventStoreKey) throws ObjectStoreException
    {
        final MuleEvent persistedEvent = getUntilSuccessfulConfiguration().getObjectStore().retrieve(eventStoreKey);
        final MuleEvent mutableEvent = threadSafeCopy(persistedEvent);
        processEvent(mutableEvent);
        removeFromStore(eventStoreKey);
    }

    protected MuleEvent threadSafeCopy(final MuleEvent event)
    {
        final DefaultMuleMessage message = new DefaultMuleMessage(event.getMessage().getPayload(),
                                                                  event.getMessage(), getUntilSuccessfulConfiguration().getMuleContext());

        return new DefaultMuleEvent(message, event);
    }

    private void ensurePayloadSerializable(final MuleEvent event) throws Exception
    {
        final MuleMessage message = event.getMessage();
        if (message instanceof DefaultMuleMessage)
        {
            if (((DefaultMuleMessage) message).isConsumable())
            {
                message.getPayloadAsBytes();
            }
            else
            {
                if (!(message.getPayload() instanceof Serializable))
                {
                    throw new NotSerializableException(message.getPayload().getClass().getCanonicalName());
                }
            }
        }
        else
        {
            message.getPayloadAsBytes();
        }
    }

    @Override
    public void setMessagingExceptionHandler(MessagingExceptionHandler messagingExceptionHandler)
    {
        this.messagingExceptionHandler = messagingExceptionHandler;
    }

}
TOP

Related Classes of org.mule.routing.AsynchronousUntilSuccessfulProcessingStrategy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.