org.mule.util.queue.DualRandomAccessFileQueueStoreDelegate.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.util.queue.DualRandomAccessFileQueueStoreDelegate.java

Source

/*
 * 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.util.queue;

import org.mule.api.MuleContext;
import org.mule.api.serialization.ObjectSerializer;
import org.mule.util.Preconditions;

import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

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

/**
 * {@link TransactionalQueueStoreDelegate} implementation using two files for storing the
 * queue data.
 * <p/>
 * Entries are stored in the queue file until a certain size in the file. After that size is reached a new file is created
 * and used to store new entries until the previous file queue entries are consumed, in which case the file is cleaned and reused
 * for new entries once the second files gets full.
 */
public class DualRandomAccessFileQueueStoreDelegate extends AbstractQueueStoreDelegate
        implements TransactionalQueueStoreDelegate {

    public static final String MAX_LENGTH_PER_FILE_PROPERTY_KEY = "mule.queue.maxlength";
    private static final int ONE_MEGABYTE = 1024 * 1024;
    private static final String QUEUE_STORE_DIRECTORY = "queuestore";
    private static final Integer MAXIMUM_QUEUE_FILE_SIZE_IN_BYTES = Integer.valueOf(
            System.getProperty(MAX_LENGTH_PER_FILE_PROPERTY_KEY, Integer.valueOf(ONE_MEGABYTE).toString()));
    private static final String QUEUE_STORE_1_SUFFIX = "-1";
    private static final String QUEUE_STORE_2_SUFFIX = "-2";
    private static final Object QUEUE_DATA_CONTROL_SUFFIX = "-crl";

    protected final Log logger = LogFactory.getLog(this.getClass());
    private final MuleContext muleContext;
    private final ObjectSerializer serializer;
    private final ReadWriteLock filesLock;
    private final QueueControlDataFile queueControlDataFile;
    private RandomAccessFileQueueStore writeFile;
    private RandomAccessFileQueueStore readFile;
    private RandomAccessFileQueueStore randomAccessFileQueueStore1;
    private RandomAccessFileQueueStore randomAccessFileQueueStore2;

    public DualRandomAccessFileQueueStoreDelegate(String queueName, String workingDirectory,
            MuleContext muleContext, int capacity) {
        super(capacity);
        this.muleContext = muleContext;
        serializer = muleContext.getObjectSerializer();
        File queuesDirectory = getQueuesDirectory(workingDirectory);
        if (!queuesDirectory.exists()) {
            Preconditions.checkState(queuesDirectory.mkdirs(),
                    "Could not create queue store directory " + queuesDirectory.getAbsolutePath());
        }
        randomAccessFileQueueStore1 = new RandomAccessFileQueueStore(
                new QueueFileProvider(queuesDirectory, queueName + QUEUE_STORE_1_SUFFIX));
        randomAccessFileQueueStore2 = new RandomAccessFileQueueStore(
                new QueueFileProvider(queuesDirectory, queueName + QUEUE_STORE_2_SUFFIX));
        queueControlDataFile = new QueueControlDataFile(
                new QueueFileProvider(queuesDirectory, queueName + QUEUE_DATA_CONTROL_SUFFIX),
                randomAccessFileQueueStore1.getFile(), randomAccessFileQueueStore2.getFile());
        writeFile = queueControlDataFile.getCurrentWriteFile().getAbsolutePath()
                .equals(randomAccessFileQueueStore1.getFile().getAbsolutePath()) ? randomAccessFileQueueStore1
                        : randomAccessFileQueueStore2;
        readFile = queueControlDataFile.getCurrentReadFile().getAbsolutePath()
                .equals(randomAccessFileQueueStore1.getFile().getAbsolutePath()) ? randomAccessFileQueueStore1
                        : randomAccessFileQueueStore2;
        filesLock = new ReentrantReadWriteLock();

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Queue %s has %s messages", queueName, getSize()));
        }
    }

    //only for testing.
    QueueControlDataFile getQueueControlDataFile() {
        return queueControlDataFile;
    }

    private static File getQueuesDirectory(String workingDirectory) {
        return new File(workingDirectory + File.separator + QUEUE_STORE_DIRECTORY);
    }

    public static File getFirstQueueFileForTesting(String queueName, String workingDirectory) {
        return new File(getQueuesDirectory(workingDirectory), queueName + QUEUE_STORE_1_SUFFIX);
    }

    @Override
    protected void addFirst(Serializable item) throws InterruptedException {
        switchWriteFileIfFull();
        byte[] serialiazedObject = serializer.serialize(item);
        readFile.addFirst(serialiazedObject);
    }

    @Override
    protected void add(Serializable item) {
        switchWriteFileIfFull();
        byte[] serialiazedObject = serializer.serialize(item);
        writeFile.addLast(serialiazedObject);
    }

    @Override
    protected Serializable removeFirst() throws InterruptedException {
        Serializable value = getFirst();
        if (value != null) {
            readFile.removeFirst();
        }
        return value;
    }

    @Override
    protected Serializable getFirst() throws InterruptedException {
        if (isEmpty()) {
            return null;
        }
        Lock lock = filesLock.readLock();
        lock.lock();
        byte[] bytes;
        try {
            if (readFile.isEmpty()) {
                readFile.clear();
                switchReadFile();
            }
            bytes = readFile.getFirst();
        } finally {
            lock.unlock();
        }
        return deserialize(bytes);
    }

    @Override
    public int size() {
        Lock lock = filesLock.readLock();
        lock.lock();
        try {
            return randomAccessFileQueueStore1.getSize() + randomAccessFileQueueStore2.getSize();
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected boolean isEmpty() {
        Lock lock = filesLock.readLock();
        lock.lock();
        try {
            return randomAccessFileQueueStore1.isEmpty() && randomAccessFileQueueStore2.isEmpty();
        } finally {
            lock.unlock();
        }
    }

    @Override
    public synchronized void doClear() {
        Lock lock = filesLock.readLock();
        lock.lock();
        try {
            randomAccessFileQueueStore1.clear();
            randomAccessFileQueueStore2.clear();
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected boolean doAddAll(Collection<? extends Serializable> items) {
        Lock lock = filesLock.readLock();
        lock.lock();
        try {
            for (Serializable item : items) {
                add(item);
            }
        } finally {
            lock.unlock();
        }
        return true;
    }

    public Collection<Serializable> allElements() {
        List<Serializable> elements = new LinkedList<Serializable>();
        elements.addAll(deserializeValues(randomAccessFileQueueStore1.allElements()));
        elements.addAll(deserializeValues(randomAccessFileQueueStore2.allElements()));
        return elements;
    }

    private Collection<Serializable> deserializeValues(Collection<byte[]> valuesAsBytes) {
        List<Serializable> values = new ArrayList<Serializable>(valuesAsBytes.size());
        for (byte[] valueAsByte : valuesAsBytes) {
            try {
                values.add(deserialize(valueAsByte));
            } catch (Exception e) {
                logger.warn("Failure trying to deserialize value " + e.getMessage());
                if (logger.isDebugEnabled()) {
                    logger.debug(e);
                }
            }
        }
        return values;
    }

    private Serializable deserialize(byte[] valuesAsBytes) {
        return serializer.deserialize(valuesAsBytes);
    }

    public void remove(Serializable value) {
        RawDataSelector rawDataSelector = createDataSelector(value);
        if (!randomAccessFileQueueStore1.remove(rawDataSelector)) {
            randomAccessFileQueueStore2.remove(rawDataSelector);
        }
    }

    private RawDataSelector createDataSelector(final Serializable value) {
        return new RawDataSelector() {
            @Override
            public boolean isSelectedData(byte[] data) {
                return deserialize(data).equals(value);
            }
        };
    }

    @Override
    public boolean contains(Serializable value) {
        Lock lock = filesLock.readLock();
        lock.lock();
        try {
            final RawDataSelector dataSelector = createDataSelector(value);
            if (!randomAccessFileQueueStore1.contains(dataSelector)) {
                return randomAccessFileQueueStore2.contains(dataSelector);
            }
        } finally {
            lock.unlock();
        }
        return true;
    }

    @Override
    public void close() {
        Lock lock = filesLock.readLock();
        lock.lock();
        try {
            doClose();
        } finally {
            lock.unlock();
        }
    }

    private void switchReadFile() {
        if (logger.isDebugEnabled()) {
            logger.debug("switching read file. Random 1 size: " + randomAccessFileQueueStore1.getSize()
                    + " , Random 2 size: " + randomAccessFileQueueStore2.getSize());
        }
        readFile = nextReadFile();
        queueControlDataFile.writeControlData(writeFile.getFile(), readFile.getFile());
    }

    private void switchWriteFileIfFull() {
        if (writeFile.getLength() >= MAXIMUM_QUEUE_FILE_SIZE_IN_BYTES) {
            Lock lock = filesLock.writeLock();
            lock.lock();
            try {
                if (writeFile.getLength() >= MAXIMUM_QUEUE_FILE_SIZE_IN_BYTES) {
                    if (randomAccessFileQueueStore1.getLength() >= MAXIMUM_QUEUE_FILE_SIZE_IN_BYTES
                            && randomAccessFileQueueStore2.getLength() >= MAXIMUM_QUEUE_FILE_SIZE_IN_BYTES) {
                        return;
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug(
                                "switching write file. Random 1 size: " + randomAccessFileQueueStore1.getLength()
                                        + " , Random 2 size: " + randomAccessFileQueueStore2.getLength());
                    }
                    writeFile = (writeFile == randomAccessFileQueueStore1 ? randomAccessFileQueueStore2
                            : randomAccessFileQueueStore1);
                    queueControlDataFile.writeControlData(writeFile.getFile(), readFile.getFile());
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private RandomAccessFileQueueStore nextReadFile() {
        return readFile == randomAccessFileQueueStore1 ? randomAccessFileQueueStore2 : randomAccessFileQueueStore1;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void dispose() {
        Lock lock = filesLock.writeLock();
        lock.lock();
        try {
            doClose();
            delete();
        } finally {
            lock.unlock();
        }
    }

    private void delete() {
        Lock lock = filesLock.writeLock();
        lock.lock();
        try {
            randomAccessFileQueueStore1.delete();
            randomAccessFileQueueStore2.delete();
            queueControlDataFile.delete();
        } finally {
            lock.unlock();
        }
    }

    private void doClose() {
        randomAccessFileQueueStore1.close();
        randomAccessFileQueueStore2.close();
        queueControlDataFile.close();
    }

}