Package org.activemq.store.bdb

Source Code of org.activemq.store.bdb.BDbMessageStore

/**
*
* 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.activemq.store.bdb;

import java.io.IOException;

import javax.jms.JMSException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.activemq.AlreadyClosedException;
import org.activemq.io.WireFormat;
import org.activemq.message.ActiveMQMessage;
import org.activemq.message.MessageAck;
import org.activemq.service.MessageContainer;
import org.activemq.service.MessageIdentity;
import org.activemq.store.MessageStore;
import org.activemq.store.RecoveryListener;
import org.activemq.util.JMSExceptionHelper;

import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.SecondaryConfig;
import com.sleepycat.je.SecondaryCursor;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.Transaction;

/**
* @version $Revision: 1.1 $
*/
public class BDbMessageStore implements MessageStore {
    private static final Log log = LogFactory.getLog(BDbMessageStore.class);

    private Database database;
    private WireFormat wireFormat;
    private SecondaryDatabase secondaryDatabase;
    private SecondaryConfig secondaryConfig;
    private SequenceNumberCreator sequenceNumberCreator;
    private MessageContainer container;
    private CursorConfig cursorConfig;


    public BDbMessageStore(Database database, SecondaryDatabase secondaryDatabase, SecondaryConfig secondaryConfig, SequenceNumberCreator sequenceNumberCreator, WireFormat wireFormat) {
        this.database = database;
        this.secondaryDatabase = secondaryDatabase;
        this.secondaryConfig = secondaryConfig;
        this.sequenceNumberCreator = sequenceNumberCreator;
        this.wireFormat = wireFormat;
    }

    public void setMessageContainer(MessageContainer container) {
        this.container = container;
    }

    public void addMessage(ActiveMQMessage message) throws JMSException {
        checkClosed();
        String messageID = message.getJMSMessageID();
        try {
            Transaction transaction = BDbHelper.getTransaction();
            DatabaseEntry key = createKey(messageID);
            DatabaseEntry value = new DatabaseEntry(asBytes(message));
            database.put(transaction, key, value);

            MessageIdentity answer = message.getJMSMessageIdentity();
            answer.setSequenceNumber(sequenceNumberCreator.getLastKey());
        }
        catch (DatabaseException e) {
            throw JMSExceptionHelper.newJMSException("Failed to broker message: " + messageID + " in container: " + e, e);
        }
        catch (IOException e) {
            throw JMSExceptionHelper.newJMSException("Failed to broker message: " + messageID + " in container: " + e, e);
        }
    }

    public ActiveMQMessage getMessage(MessageIdentity identity) throws JMSException {
        checkClosed();
        ActiveMQMessage answer = null;
        String messageID = identity.getMessageID();
        try {
            DatabaseEntry key = createKey(messageID);
            DatabaseEntry value = new DatabaseEntry();
            if (database.get(null, key, value, null) == OperationStatus.SUCCESS) {
                answer = extractMessage(value);
            }
            return answer;
        }
        catch (DatabaseException e) {
            throw JMSExceptionHelper.newJMSException("Failed to peek next message after: " + messageID + " from container: " + e, e);
        }
        catch (IOException e) {
            throw JMSExceptionHelper.newJMSException("Failed to broker message: " + messageID + " in container: " + e, e);
        }
    }

    public void removeMessage(MessageAck ack) throws JMSException {
        checkClosed();
        MessageIdentity identity = ack.getMessageIdentity();
        String messageID = identity.getMessageID();
        try {
            Transaction transaction = BDbHelper.getTransaction();

            // we need to find the alternative primary key for the given messageID
            DatabaseEntry sequenceNumber = getSequenceNumberKey(identity);

            //System.out.println("Deleting sequenceNumber: " + BDbHelper.longFromBytes(sequenceNumber.getData()));

            sequenceNumberCreator.setDeleteKey(sequenceNumber);

            OperationStatus status = secondaryDatabase.delete(transaction, sequenceNumber);
            if (status != OperationStatus.SUCCESS) {
                log.error("Could not delete sequenece number for: " + identity + " status: " + status);
            }
        }
        catch (DatabaseException e) {
            throw JMSExceptionHelper.newJMSException("Failed to delete message: " + messageID + " from container: " + e, e);
        }
    }

    public void recover(RecoveryListener listener) throws JMSException {
        checkClosed();
        SecondaryCursor cursor = null;
        try {
            cursor = secondaryDatabase.openSecondaryCursor(BDbHelper.getTransaction(), cursorConfig);
            DatabaseEntry sequenceNumberEntry = new DatabaseEntry();
            DatabaseEntry keyEntry = new DatabaseEntry();
            DatabaseEntry valueEntry = new DatabaseEntry();
            OperationStatus status = cursor.getFirst(sequenceNumberEntry, keyEntry, valueEntry, LockMode.DEFAULT);
            while (status == OperationStatus.SUCCESS) {
                String messageID = extractString(keyEntry);
                listener.recoverMessage(new MessageIdentity(messageID, sequenceNumberEntry));
                status = cursor.getNext(sequenceNumberEntry, keyEntry, valueEntry, LockMode.DEFAULT);
            }
            if (status != OperationStatus.NOTFOUND) {
                log.warn("Unexpected status code while recovering: " + status);
            }
        }
        catch (DatabaseException e) {
            throw JMSExceptionHelper.newJMSException("Failed to recover container. Reason: " + e, e);
        }
        finally {
            if (cursor != null) {
                try {
                    cursor.close();
                }
                catch (DatabaseException e) {
                    log.warn("Caught exception closing cursor: " + e, e);
                }
            }
        }

    }


    public void start() throws JMSException {
    }

    public void stop() throws JMSException {
        JMSException firstException = BDbPersistenceAdapter.closeDatabase(secondaryDatabase, null);
        firstException = BDbPersistenceAdapter.closeDatabase(database, firstException);

        secondaryDatabase = null;
        database = null;

        if (firstException != null) {
            throw firstException;
        }
    }

    // Implementation methods
    //-------------------------------------------------------------------------
    protected SecondaryDatabase getSecondaryDatabase() {
        return secondaryDatabase;
    }

    protected Database getDatabase() {
        return database;
    }

    public CursorConfig getCursorConfig() {
        return cursorConfig;
    }

    public MessageContainer getContainer() {
        return container;
    }


    protected void checkClosed() throws AlreadyClosedException {
        if (database == null) {
            throw new AlreadyClosedException("Berkeley DB MessageStore");
        }
    }

    /**
     * Returns the sequence number key for the given message identity. If the
     * sequence number is not available it will be queried (which is slow & will generate a warning
     * as it is not recommended) and then it'll be cached inside the MessageIdentity
     *
     * @param identity
     * @return
     * @throws DatabaseException
     */
    protected DatabaseEntry getSequenceNumberKey(MessageIdentity identity) throws DatabaseException {
        DatabaseEntry sequenceNumber = (DatabaseEntry) identity.getSequenceNumber();
        if (sequenceNumber == null) {
            sequenceNumber = findSequenceNumber(identity.getMessageID());
        }
        return sequenceNumber;
    }

    protected DatabaseEntry createKey(String messageID) {
        DatabaseEntry key = new DatabaseEntry(asBytes(messageID));
        return key;
    }


    /**
     * Iterates through from the start of the collection until the given message ID is found
     *
     * @param messageID
     * @return
     */
    protected DatabaseEntry findSequenceNumber(String messageID) throws DatabaseException {
        log.warn("Having to table scan to find the sequence number for messageID: " + messageID);

        SecondaryCursor cursor = null;
        try {
            cursor = secondaryDatabase.openSecondaryCursor(BDbHelper.getTransaction(), cursorConfig);
            DatabaseEntry sequenceNumberEntry = new DatabaseEntry();
            DatabaseEntry keyEntry = new DatabaseEntry();
            DatabaseEntry valueEntry = new DatabaseEntry();
            OperationStatus status = cursor.getFirst(sequenceNumberEntry, keyEntry, valueEntry, LockMode.DEFAULT);
            while (status == OperationStatus.SUCCESS) {
                String value = extractString(keyEntry);
                if (messageID.equals(value)) {
                    return sequenceNumberEntry;
                }
                status = cursor.getNext(sequenceNumberEntry, keyEntry, valueEntry, LockMode.DEFAULT);
            }
        }
        finally {
            if (cursor != null) {
                try {
                    cursor.close();
                }
                catch (DatabaseException e) {
                    log.warn("Caught exception closing cursor: " + e, e);
                }
            }
        }
        return null;
    }

    protected String extractString(DatabaseEntry entry) {
        return new String(entry.getData(), entry.getOffset(), entry.getSize());
    }

    protected ActiveMQMessage extractMessage(DatabaseEntry value) throws IOException {
        // we must synchronize access to wireFormat
        synchronized (wireFormat) {
            return (ActiveMQMessage) wireFormat.fromBytes(value.getData(), value.getOffset(), value.getSize());
        }
    }

    protected byte[] asBytes(ActiveMQMessage message) throws IOException, JMSException {
        // we must synchronize access to wireFormat
        synchronized (wireFormat) {
            return wireFormat.toBytes(message);
        }
    }

    protected byte[] asBytes(String messageID) {
        return messageID.getBytes();
    }

    /**
     * @see org.activemq.store.MessageStore#removeAllMessages()
     */
    public void removeAllMessages() throws JMSException {
        //TODO: implement me.
    }

}
TOP

Related Classes of org.activemq.store.bdb.BDbMessageStore

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.