Package org.codehaus.activemq.broker.impl

Source Code of org.codehaus.activemq.broker.impl.DefaultBroker

/**
*
* Copyright 2004 Protique Ltd
*
* Licensed 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.codehaus.activemq.broker.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.broker.Broker;
import org.codehaus.activemq.broker.BrokerClient;
import org.codehaus.activemq.capacity.DelegateCapacityMonitor;
import org.codehaus.activemq.message.ActiveMQMessage;
import org.codehaus.activemq.message.ActiveMQXid;
import org.codehaus.activemq.message.ConsumerInfo;
import org.codehaus.activemq.message.MessageAck;
import org.codehaus.activemq.message.util.MemoryBoundedQueueManager;
import org.codehaus.activemq.service.MessageContainerManager;
import org.codehaus.activemq.service.Transaction;
import org.codehaus.activemq.service.TransactionManager;
import org.codehaus.activemq.service.boundedvm.TransientTopicBoundedMessageManager;
import org.codehaus.activemq.service.impl.DurableTopicMessageContainerManager;
import org.codehaus.activemq.service.impl.MessageAckTransactionTask;
import org.codehaus.activemq.service.impl.QueueMessageContainerManager;
import org.codehaus.activemq.service.impl.RedeliverMessageTransactionTask;
import org.codehaus.activemq.service.impl.SendMessageTransactionTask;
import org.codehaus.activemq.store.PersistenceAdapter;
import org.codehaus.activemq.store.PreparedTransactionStore;
import org.codehaus.activemq.store.vm.VMPersistenceAdapter;
import org.codehaus.activemq.store.vm.VMTransactionManager;
import org.codehaus.activemq.util.Callback;
import org.codehaus.activemq.util.ExceptionTemplate;
import org.codehaus.activemq.util.JMSExceptionHelper;

import javax.jms.JMSException;
import javax.transaction.xa.XAException;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* The default {@link Broker} implementation
*
* @version $Revision: 1.11 $
*/
public class DefaultBroker extends DelegateCapacityMonitor implements Broker {

    private static final Log log = LogFactory.getLog(DefaultBroker.class);

    protected static final String PROPERTY_STORE_DIRECTORY = "activemq.store.dir";
    protected static final String PERSISTENCE_ADAPTER_PROPERTY = "activemq.persistenceAdapter";

    protected static final Class[] NEWINSTANCE_PARAMETER_TYPES = {File.class};

    private static final long DEFAULT_MAX_MEMORY_USAGE = 20 * 1024 * 1024; //20mb

    private PersistenceAdapter persistenceAdapter;
    private TransactionManager transactionManager;
    private MessageContainerManager[] containerManagers;
    private File tempDir;
    private MemoryBoundedQueueManager memoryManager;
    private PreparedTransactionStore preparedTransactionStore;
    private final String brokerName;


    public DefaultBroker(String brokerName) {
        this.brokerName = brokerName;
        memoryManager = new MemoryBoundedQueueManager("Broker Memory Manager", DEFAULT_MAX_MEMORY_USAGE);
        setDelegate(memoryManager);
    }

    public DefaultBroker(String brokerName, PersistenceAdapter persistenceAdapter) {
        this(brokerName);
        this.persistenceAdapter = persistenceAdapter;
    }

    /**
     * Start this Service
     *
     * @throws JMSException
     */
    public void start() throws JMSException {
        if (persistenceAdapter == null) {
            persistenceAdapter = createPersistenceAdapter();
        }
        persistenceAdapter.start();

        if (transactionManager == null) {
            preparedTransactionStore = persistenceAdapter.createPreparedTransactionStore();
            transactionManager = new VMTransactionManager(this, preparedTransactionStore);
        }
        transactionManager.start();

        // force containers to be created
        getContainerManagers();


        for (int i = 0; i < containerManagers.length; i++) {
            containerManagers[i].start();
        }
    }


    /**
     * stop this Service
     *
     * @throws JMSException
     */

    public void stop() throws JMSException {
        ExceptionTemplate template = new ExceptionTemplate();

        for (int i = 0; i < containerManagers.length; i++) {
            final MessageContainerManager containerManager = containerManagers[i];
            template.run(new Callback() {
                public void execute() throws Throwable {
                    containerManager.stop();
                }
            });
        }
        if (transactionManager != null) {
            template.run(new Callback() {
                public void execute() throws Throwable {
                    transactionManager.stop();
                }
            });
        }

        template.run(new Callback() {
            public void execute() throws Throwable {
                persistenceAdapter.stop();
            }
        });

        template.throwJMSException();
    }

    /**
     * Acknowledge consumption of a message by the Message Consumer
     *
     * @param client
     * @param ack
     * @throws JMSException
     */
    public void acknowledgeMessage(BrokerClient client, MessageAck ack) throws JMSException {
        for (int i = 0; i < containerManagers.length; i++) {
            containerManagers[i].acknowledgeMessage(client, ack);
        }
    }

    /**
     * Acknowledge consumption of a message within a transaction
     *
     * @param client
     * @param transactionId
     * @param ack
     */
    public void acknowledgeTransactedMessage(final BrokerClient client, final String transactionId, final MessageAck ack) throws JMSException {
        Transaction transaction;
        if (ack.isXaTransacted()) {
            try {
                transaction = transactionManager.getXATransaction(new ActiveMQXid(transactionId));
            }
            catch (XAException e) {
                throw (JMSException) new JMSException(e.getMessage()).initCause(e);
            }
        }
        else {
            transaction = transactionManager.getLocalTransaction(transactionId);
        }
        transaction.addPostCommitTask(new MessageAckTransactionTask(client, ack));
        transaction.addPostRollbackTask(new RedeliverMessageTransactionTask(client, ack));

        // we need to tell the dispatcher that we can now accept another message
        // even though we don't really ack the message until the commit
        // this is because if we have a prefetch value of 1, we can never consume 2 messages
        // in a transaction, since the ack for the first message never arrives until the commit
        for (int i = 0; i < containerManagers.length; i++) {
            containerManagers[i].acknowledgeTransactedMessage(client, transactionId, ack);
        }
    }

    /**
     * send a message to the broker
     *
     * @param client
     * @param message
     * @throws JMSException
     */
    public void sendMessage(BrokerClient client, ActiveMQMessage message) throws JMSException {
        checkValid();
        if (message.getJMSMessageID() == null) {
            throw new JMSException("No messageID specified for the Message");
        }
        for (int i = 0; i < containerManagers.length; i++) {
            containerManagers[i].sendMessage(client, message);
        }
    }

    /**
     * send a message to the broker within a transaction
     *
     * @param client
     * @param transactionId
     * @param message
     */
    public void sendTransactedMessage(final BrokerClient client, final String transactionId, final ActiveMQMessage message) throws JMSException {
        Transaction transaction;
        if (message.isXaTransacted()) {
            try {
                transaction = transactionManager.getXATransaction(new ActiveMQXid(transactionId));
            }
            catch (XAException e) {
                throw (JMSException) new JMSException(e.getMessage()).initCause(e);
            }
        }
        else {
            transaction = transactionManager.getLocalTransaction(transactionId);
        }

        transaction.addPostCommitTask(new SendMessageTransactionTask(client, message));
    }

    /**
     * Add an active message consumer
     *
     * @param client
     * @param info
     * @throws JMSException
     */
    public void addMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
        validateConsumer(info);
        MessageContainerManager[] array = getContainerManagers();
        for (int i = 0; i < array.length; i++) {
            array[i].addMessageConsumer(client, info);
        }
    }

    /**
     * remove an active message consumer
     *
     * @param client
     * @param info
     * @throws JMSException
     */
    public void removeMessageConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
        validateConsumer(info);
        for (int i = 0; i < containerManagers.length; i++) {
            containerManagers[i].removeMessageConsumer(client, info);
        }
    }

    public void redeliverMessage(BrokerClient client, MessageAck ack) throws JMSException {
        for (int i = 0; i < containerManagers.length; i++) {
            containerManagers[i].redeliverMessage(client, ack);
        }
    }

    /**
     * Delete a durable subscriber
     *
     * @param clientId
     * @param subscriberName
     * @throws JMSException if the subscriber doesn't exist or is still active
     */
    public void deleteSubscription(String clientId, String subscriberName) throws JMSException {
        for (int i = 0; i < containerManagers.length; i++) {
            containerManagers[i].deleteSubscription(clientId, subscriberName);
        }
    }


    /**
     * Start a transaction.
     *
     * @see org.codehaus.activemq.broker.Broker#startTransaction(org.codehaus.activemq.broker.BrokerClient, java.lang.String)
     */
    public void startTransaction(BrokerClient client, String transactionId) throws JMSException {
        transactionManager.createLocalTransaction(client, transactionId);
    }

    /**
     * commit a transaction
     *
     * @param client
     * @param transactionId
     * @throws JMSException
     */
    public void commitTransaction(BrokerClient client, String transactionId) throws JMSException {
        try {
            for (int i = 0; i < containerManagers.length; i++) {
                containerManagers[i].commitTransaction(client, transactionId);
            }
            Transaction transaction = transactionManager.getLocalTransaction(transactionId);
            transaction.commit(true);
        }
        catch (XAException e) {
            // TODO: I think the XAException should propagate all the way to the client.
            throw (JMSException) new JMSException(e.getMessage()).initCause(e);
        }
    }

    /**
     * rollback a transaction
     *
     * @param client
     * @param transactionId
     */
    public void rollbackTransaction(BrokerClient client, String transactionId) throws JMSException {
        try {
            for (int i = 0; i < containerManagers.length; i++) {
                containerManagers[i].rollbackTransaction(client, transactionId);
            }
            Transaction transaction = transactionManager.getLocalTransaction(transactionId);
            transaction.rollback();
        }
        catch (XAException e) {
            // TODO: I think the XAException should propagate all the way to the client.
            throw (JMSException) new JMSException(e.getMessage()).initCause(e);
        }
    }

    /**
     * Starts an XA Transaction.
     *
     * @see org.codehaus.activemq.broker.Broker#startTransaction(org.codehaus.activemq.broker.BrokerClient, org.codehaus.activemq.message.ActiveMQXid)
     */
    public void startTransaction(BrokerClient client, ActiveMQXid xid) throws XAException {
        transactionManager.createXATransaction(client, xid);
    }

    /**
     * Prepares an XA Transaciton.
     *
     * @see org.codehaus.activemq.broker.Broker#prepareTransaction(org.codehaus.activemq.broker.BrokerClient, org.codehaus.activemq.message.ActiveMQXid)
     */
    public int prepareTransaction(BrokerClient client, ActiveMQXid xid) throws XAException {
        Transaction transaction = transactionManager.getXATransaction(xid);
        return transaction.prepare();
    }

    /**
     * Rollback an XA Transaction.
     *
     * @see org.codehaus.activemq.broker.Broker#rollbackTransaction(org.codehaus.activemq.broker.BrokerClient, org.codehaus.activemq.message.ActiveMQXid)
     */
    public void rollbackTransaction(BrokerClient client, ActiveMQXid xid) throws XAException {
        Transaction transaction = transactionManager.getXATransaction(xid);
        transaction.rollback();
    }

    /**
     * Commit an XA Transaction.
     *
     * @see org.codehaus.activemq.broker.Broker#commitTransaction(org.codehaus.activemq.broker.BrokerClient, org.codehaus.activemq.message.ActiveMQXid, boolean)
     */
    public void commitTransaction(BrokerClient client, ActiveMQXid xid, boolean onePhase) throws XAException {
        Transaction transaction = transactionManager.getXATransaction(xid);
        transaction.commit(onePhase);
    }

    /**
     * A hint to the broker that an BrokerClient has stopped
     * This enables the broker to clean-up any outstanding processing
     * that may be outstanding
     *
     * @param client
     */
    public void cleanUpClient(BrokerClient client) throws JMSException {
        if (transactionManager != null) {
            transactionManager.cleanUpClient(client);
        }
    }

    /**
     * Gets the prepared XA transactions.
     *
     * @see org.codehaus.activemq.broker.Broker#getPreparedTransactions(org.codehaus.activemq.broker.BrokerClient)
     */
    public ActiveMQXid[] getPreparedTransactions(BrokerClient client) throws XAException {
        return transactionManager.getPreparedXATransactions();
    }

    // Properties
    //-------------------------------------------------------------------------

    /**
     * Get a temp directory - used for spooling
     *
     * @return a File ptr to the directory
     */
    public File getTempDir() {
        if (tempDir == null) {
            String dirName = System.getProperty("activemq.store.tempdir", "ActiveMQTemp");
            tempDir = new File(dirName);
        }
        return tempDir;
    }

    public String getBrokerName() {
        return brokerName;
    }

    public void setTempDir(File tempDir) {
        this.tempDir = tempDir;
    }

    public MessageContainerManager[] getContainerManagers() {
        if (containerManagers == null) {
            containerManagers = createContainerManagers();
        }
        return containerManagers;
    }

    public void setContainerManagers(MessageContainerManager[] containerManagers) {
        this.containerManagers = containerManagers;
    }

    public PersistenceAdapter getPersistenceAdapter() {
        return persistenceAdapter;
    }

    public void setPersistenceAdapter(PersistenceAdapter persistenceAdapter) {
        this.persistenceAdapter = persistenceAdapter;
    }

    public TransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public PreparedTransactionStore getPreparedTransactionStore() {
        return preparedTransactionStore;
    }

    public void setPreparedTransactionStore(PreparedTransactionStore preparedTransactionStore) {
        this.preparedTransactionStore = preparedTransactionStore;
    }

    /**
     * @return Returns the maximumMemoryUsage.
     */
    public long getMaximumMemoryUsage() {
        return memoryManager.getValueLimit();
    }

    /**
     * @param maximumMemoryUsage The maximumMemoryUsage to set.
     */
    public void setMaximumMemoryUsage(long maximumMemoryUsage) {
        this.memoryManager.setValueLimit(maximumMemoryUsage);
    }

   
    // Implementation methods
    //-------------------------------------------------------------------------


    /**
     * Factory method to create a default persistence adapter
     *
     * @return
     */
    protected PersistenceAdapter createPersistenceAdapter() throws JMSException {
        File directory = new File(getStoreDirectory());

        // lets use reflection to avoid runtime dependency on persistence libraries
        PersistenceAdapter answer = null;
        String property = System.getProperty(PERSISTENCE_ADAPTER_PROPERTY);
        if (property != null) {
            answer = tryCreatePersistenceAdapter(property, directory, false);
        }
        if (answer == null) {
            answer = tryCreatePersistenceAdapter("org.codehaus.activemq.store.jdbm.JdbmPersistenceAdapter", directory, true);
        }
        if (answer == null) {
            answer = tryCreatePersistenceAdapter("org.codehaus.activemq.store.bdb.BDbPersistenceAdapter", directory, true);
        }
        if (answer != null) {
            return answer;
        }
        else {
            log.warn("Neither JDBM or BDB on the classpath or property '" + PERSISTENCE_ADAPTER_PROPERTY
                    + "' not specified so defaulting to use RAM based message persistence");
            return new VMPersistenceAdapter();
        }
    }

    protected PersistenceAdapter tryCreatePersistenceAdapter(String className, File directory, boolean ignoreErrors) throws JMSException {
        Class adapterClass = loadClass(className, ignoreErrors);
        if (adapterClass != null) {

            try {
                Method method = adapterClass.getMethod("newInstance", NEWINSTANCE_PARAMETER_TYPES);
                PersistenceAdapter answer = (PersistenceAdapter) method.invoke(null, new Object[]{directory});
                log.info("Using persistence adapter: " + adapterClass.getName());
                return answer;
            }
            catch (InvocationTargetException e) {
                Throwable cause = e.getTargetException();
                if (cause != null) {
                    if (cause instanceof JMSException) {
                        throw (JMSException) cause;
                    }
                    else {
                        if (cause instanceof Exception) {
                            throw createInstantiateAdapterException(adapterClass, (Exception) cause);
                        }
                    }
                }
                if (!ignoreErrors) {
                    throw createInstantiateAdapterException(adapterClass, e);
                }
            }
            catch (Throwable e) {
                if (!ignoreErrors) {
                    throw createInstantiateAdapterException(adapterClass, e);
                }
            }
        }
        return null;
    }

    protected JMSException createInstantiateAdapterException(Class adapterClass, Throwable e) {
        return JMSExceptionHelper.newJMSException("Could not instantiate instance of "
                + adapterClass.getName() + ". Reason: " + e, e);
    }

    /**
     * Tries to load the given class from the current context class loader or
     * class loader which loaded us or return null if the class could not be found
     */
    protected Class loadClass(String name, boolean ignoreErrors) throws JMSException {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(name);
        }
        catch (ClassNotFoundException e) {
            try {
                return getClass().getClassLoader().loadClass(name);
            }
            catch (ClassNotFoundException e2) {
                if (ignoreErrors) {
                    log.trace("Could not find class: " + name + " on the classpath");
                    return null;
                }
                else {
                    throw JMSExceptionHelper.newJMSException("Could not find class: " + name + " on the classpath. Reason: " + e, e);
                }
            }
        }
    }

    protected String getStoreDirectory() {
        return System.getProperty(PROPERTY_STORE_DIRECTORY, "ActiveMQ");
    }

    /**
     * Factory method to create the default container managers
     *
     * @return
     */
    protected MessageContainerManager[] createContainerManagers() {
        MessageContainerManager[] answer = {
            //new TransientTopicMessageContainerManager(persistenceAdapter),
            new TransientTopicBoundedMessageManager(memoryManager),
            new DurableTopicMessageContainerManager(persistenceAdapter),
            new QueueMessageContainerManager(persistenceAdapter),
        };
        return answer;
    }

    /**
     * Ensures the consumer is valid, throwing a meaningful exception if not
     *
     * @param info
     * @throws JMSException
     */
    protected void validateConsumer(ConsumerInfo info) throws JMSException {
        if (info.getConsumerId() == null) {
            throw new JMSException("No consumerId specified for the ConsumerInfo");
        }
    }

    protected void checkValid() throws JMSException {
        if (containerManagers == null) {
            throw new JMSException("This Broker has not yet been started. Ensure start() is called before invoking action methods");
        }
    }
}
TOP

Related Classes of org.codehaus.activemq.broker.impl.DefaultBroker

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.