Package org.apache.servicemix.eip.support

Source Code of org.apache.servicemix.eip.support.AbstractAggregator

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.servicemix.eip.support;

import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;

import javax.jbi.messaging.ExchangeStatus;
import javax.jbi.messaging.InOnly;
import javax.jbi.messaging.MessageExchange;
import javax.jbi.messaging.NormalizedMessage;
import javax.jbi.messaging.RobustInOnly;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.servicemix.eip.EIPEndpoint;
import org.apache.servicemix.jbi.util.MessageUtil;
import org.apache.servicemix.timers.Timer;
import org.apache.servicemix.timers.TimerListener;

/**
* Aggregator can be used to wait and combine several messages.
* This component implements the
* <a href="http://www.enterpriseintegrationpatterns.com/Aggregator.html">Aggregator</a>
* pattern.
*
* TODO: keep list of closed aggregations for a certain time
* TODO: distributed lock manager
* TODO: persistent / transactional timer
*
* @author gnodet
* @version $Revision: 376451 $
*/
public abstract class AbstractAggregator extends EIPEndpoint {

    private static final Log LOG = LogFactory.getLog(AbstractAggregator.class);

    private ExchangeTarget target;

    private boolean rescheduleTimeouts;

    private boolean synchronous;

    private ConcurrentMap<String, Boolean> closedAggregates = new ConcurrentHashMap<String, Boolean>();

    private ConcurrentMap<String, Timer> timers = new ConcurrentHashMap<String, Timer>();

    /**
     * @return the synchronous
     */
    public boolean isSynchronous() {
        return synchronous;
    }

    /**
     * @param synchronous the synchronous to set
     */
    public void setSynchronous(boolean synchronous) {
        this.synchronous = synchronous;
    }

    /**
     * @return the rescheduleTimeouts
     */
    public boolean isRescheduleTimeouts() {
        return rescheduleTimeouts;
    }

    /**
     * @param rescheduleTimeouts the rescheduleTimeouts to set
     */
    public void setRescheduleTimeouts(boolean rescheduleTimeouts) {
        this.rescheduleTimeouts = rescheduleTimeouts;
    }

    /**
     * @return the target
     */
    public ExchangeTarget getTarget() {
        return target;
    }

    /**
     * @param target the target to set
     */
    public void setTarget(ExchangeTarget target) {
        this.target = target;
    }

    /* (non-Javadoc)
     * @see org.apache.servicemix.eip.EIPEndpoint#processSync(javax.jbi.messaging.MessageExchange)
     */
    protected void processSync(MessageExchange exchange) throws Exception {
        throw new IllegalStateException();
    }

    /* (non-Javadoc)
     * @see org.apache.servicemix.eip.EIPEndpoint#processAsync(javax.jbi.messaging.MessageExchange)
     */
    protected void processAsync(MessageExchange exchange) throws Exception {
        throw new IllegalStateException();
    }

    /* (non-Javadoc)
     * @see org.apache.servicemix.common.ExchangeProcessor#process(javax.jbi.messaging.MessageExchange)
     */
    public void process(MessageExchange exchange) throws Exception {
        // Skip DONE
        if (exchange.getStatus() == ExchangeStatus.DONE) {
            return;
            // Skip ERROR
        } else if (exchange.getStatus() == ExchangeStatus.ERROR) {
            return;
            // Handle an ACTIVE exchange as a PROVIDER
        } else if (exchange.getRole() == MessageExchange.Role.PROVIDER) {
            if (!(exchange instanceof InOnly)
                && !(exchange instanceof RobustInOnly)) {
                fail(exchange, new UnsupportedOperationException("Use an InOnly or RobustInOnly MEP"));
            } else {
                processProvider(exchange);
            }
            // Handle an ACTIVE exchange as a CONSUMER
        } else if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
            done(exchange);
        }
    }

    private void processProvider(MessageExchange exchange) throws Exception {
        NormalizedMessage in = MessageUtil.copyIn(exchange);
        final String correlationId = getCorrelationID(exchange, in);
        if (correlationId == null || correlationId.length() == 0) {
            throw new IllegalArgumentException("Could not retrieve correlation id for incoming exchange");
        }
        // Load existing aggregation
        Lock lock = getLockManager().getLock(correlationId);
        lock.lock();
        try {
            Object aggregation = store.load(correlationId);
            Date timeout = null;
            // Create a new aggregate
            if (aggregation == null) {
                if (isAggregationClosed(correlationId)) {
                    // TODO: should we return an error here ?
                } else {
                    aggregation = createAggregation(correlationId);
                    timeout = getTimeout(aggregation);
                }
            } else if (isRescheduleTimeouts()) {
                Timer t = timers.remove(correlationId);
                if (t != null) {
                    t.cancel();
                }
                timeout = getTimeout(aggregation);
            }
            // If the aggregation is not closed
            if (aggregation != null) {
                if (addMessage(aggregation, in, exchange)) {
                    Timer t = timers.remove(correlationId);
                    if (t != null) {
                        t.cancel();
                    }
                    sendAggregate(correlationId, aggregation, false);
                } else {
                    store.store(correlationId, aggregation);
                    if (timeout != null) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Scheduling timeout at " + timeout + " for aggregate " + correlationId);
                        }
                        Timer t = getTimerManager().schedule(new TimerListener() {
                            public void timerExpired(Timer timer) {
                                AbstractAggregator.this.onTimeout(correlationId, timer);
                            }
                        }, timeout);
                        timers.put(correlationId, t);
                    }
                }
            }
            done(exchange);
        } finally {
            lock.unlock();
        }
    }

    protected void sendAggregate(String correlationId,
                                 Object aggregation,
                                 boolean timeout) throws Exception {
        InOnly me = getExchangeFactory().createInOnlyExchange();
        target.configureTarget(me, getContext());
        NormalizedMessage nm = me.createMessage();
        me.setInMessage(nm);
        buildAggregate(aggregation, nm, me, timeout);
        closeAggregation(correlationId);
        if (isSynchronous()) {
            sendSync(me);
        } else {
            send(me);
        }
    }

    protected void onTimeout(String correlationId, Timer timer) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Timeout expired for aggregate " + correlationId);
        }
        Lock lock = getLockManager().getLock(correlationId);
        lock.lock();
        try {
            // the timeout event could have been fired before timer was canceled
            Timer t = getTimer(correlationId);
            if (t == null || !t.equals(timer)) {
                return;
            }
            timers.remove(correlationId);
            Object aggregation = store.load(correlationId);
            if (aggregation != null) {
                sendAggregate(correlationId, aggregation, true);
            } else if (!isAggregationClosed(correlationId)) {
                throw new IllegalStateException("Aggregation is not closed, but can not be retrieved from the store");
            } else {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Aggregate " + correlationId + " is closed");
                }
            }
        } catch (Exception e) {
            LOG.info("Caught exception while processing timeout aggregation", e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * Check if the aggregation with the given correlation id is closed or not.
     * Called when the aggregation has not been found in the store.
     *
     * @param correlationId
     * @return
     */
    protected boolean isAggregationClosed(String correlationId) {
        // TODO: implement this using a persistent / cached behavior
        return closedAggregates.containsKey(correlationId);
    }

    /**
     * Mark an aggregation as closed
     * @param correlationId
     */
    protected void closeAggregation(String correlationId) {
        // TODO: implement this using a persistent / cached behavior
        closedAggregates.put(correlationId, Boolean.TRUE);
    }
   
    /**
     * Get the time-out timer for an active aggregation
     *
     * @param correlationId
     * @return
     */
    protected Timer getTimer(String correlationId) {
        return timers.get(correlationId);
    }

    /**
     * Retrieve the correlation ID of the given exchange
     * @param exchange
     * @param message
     * @return the correlationID
     * @throws Exception
     */
    protected abstract String getCorrelationID(MessageExchange exchange, NormalizedMessage message) throws Exception;

    /**
     * Creates a new empty aggregation.
     * @param correlationID
     * @return a newly created aggregation
     */
    protected abstract Object createAggregation(String correlationID) throws Exception;

    /**
     * Returns the date when the onTimeout method should be called if the aggregation is not completed yet,
     * or null if the aggregation has no timeout.
     *
     * @param aggregate
     * @return
     */
    protected abstract Date getTimeout(Object aggregate);

    /**
     * Add a newly received message to this aggregation
     *
     * @param aggregate
     * @param message
     * @param exchange
     * @return <code>true</code> if the aggregate id complete
     */
    protected abstract boolean addMessage(Object aggregate,
                                          NormalizedMessage message,
                                          MessageExchange exchange) throws Exception;

    /**
     * Fill the given JBI message with the aggregation result.
     *
     * @param aggregate
     * @param message
     * @param exchange
     * @param timeout <code>false</code> if the aggregation has completed or <code>true</code>
     *                  if this aggregation has timed out
     */
    protected abstract void buildAggregate(Object aggregate,
                                           NormalizedMessage message,
                                           MessageExchange exchange,
                                           boolean timeout) throws Exception;
}
TOP

Related Classes of org.apache.servicemix.eip.support.AbstractAggregator

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.