org.apache.excalibur.event.impl.DefaultQueue.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.excalibur.event.impl.DefaultQueue.java

Source

/* 
 * 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.excalibur.event.impl;

import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.UnboundedFifoBuffer;
import org.apache.excalibur.event.EnqueuePredicate;
import org.apache.excalibur.event.PreparedEnqueue;
import org.apache.excalibur.event.SinkException;
import org.apache.excalibur.event.SinkFullException;

import EDU.oswego.cs.dl.util.concurrent.ReentrantLock;

/**
 * The default queue implementation is a variable size queue.  This queue is
 * thread safe, however the overhead in synchronization costs a few extra
 * milliseconds.
 *
 * @author <a href="mailto:dev@avalon.apache.org">Avalon Development Team</a>
 */
public final class DefaultQueue extends AbstractQueue {
    private final Buffer m_elements;
    private final ReentrantLock m_mutex;
    protected int m_reserve;
    private final int m_maxSize;

    /**
     * Construct a new DefaultQueue with the specified number of elements.
     * if the number of elements is greater than zero, then the
     * <code>Queue</code> is bounded by that number.  Otherwise, the
     * <code>Queue</code> is not bounded at all.
     *
     * @param  size  The maximum number of elements in the <code>Queue</code>.
     *               Any number less than 1 means there is no limit.
     */
    public DefaultQueue(int size) {
        this(new ThresholdEnqueuePredicate(size));
    }

    public DefaultQueue(EnqueuePredicate predicate) {
        setEnqueuePredicate(predicate);

        m_mutex = new ReentrantLock();
        m_elements = new UnboundedFifoBuffer();
        m_reserve = 0;
        m_maxSize = -1;
    }

    /**
     * Create an unbounded DefaultQueue.
     */
    public DefaultQueue() {
        this(new NullEnqueuePredicate());
    }

    /**
     * Return the number of elements currently in the <code>Queue</code>.
     *
     * @return <code>int</code> representing the number of elements (including the reserved ones).
     */
    public int size() {
        return m_elements.size() + m_reserve;
    }

    /**
     * Return the maximum number of elements that will fit in the
     * <code>Queue</code>.  A number below 1 indecates an unbounded
     * <code>Queue</code>, which means there is no limit.
     *
     * @return <code>int</code> representing the maximum number of elements
     */
    public int maxSize() {
        return m_maxSize;
    }

    public PreparedEnqueue prepareEnqueue(final Object[] elements) throws SinkException {
        PreparedEnqueue enqueue = null;

        try {
            m_mutex.acquire();
            try {
                if (getEnqueuePredicate().accept(elements, this)) {
                    enqueue = new DefaultPreparedEnqueue(this, elements);
                } else {
                    throw new SinkFullException("Not enough room to enqueue these elements.");
                }
            } finally {
                m_mutex.release();
            }
        } catch (InterruptedException ie) {
            if (null == enqueue) {
                throw new SinkException("The mutex was interrupted before it could be released");
            }
        }

        return enqueue;
    }

    public boolean tryEnqueue(final Object element) {
        boolean success = false;

        try {
            m_mutex.acquire();
            try {
                success = getEnqueuePredicate().accept(element, this);

                if (success) {
                    m_elements.add(element);
                }
            } finally {
                m_mutex.release();
            }
        } catch (InterruptedException ie) {
        }

        return success;
    }

    public void enqueue(final Object[] elements) throws SinkException {
        final int len = elements.length;

        try {
            m_mutex.acquire();
            try {
                if (!getEnqueuePredicate().accept(elements, this)) {
                    throw new SinkFullException("Not enough room to enqueue these elements.");
                }

                for (int i = 0; i < len; i++) {
                    m_elements.add(elements[i]);
                }
            } finally {
                m_mutex.release();
            }
        } catch (InterruptedException ie) {
        }
    }

    public void enqueue(final Object element) throws SinkException {
        try {
            m_mutex.acquire();
            try {
                if (!getEnqueuePredicate().accept(element, this)) {
                    throw new SinkFullException("Not enough room to enqueue these elements.");
                }

                m_elements.add(element);
            } finally {
                m_mutex.release();
            }
        } catch (InterruptedException ie) {
        }
    }

    public Object[] dequeue(final int numElements) {
        getDequeueInterceptor().before(this);
        Object[] elements = EMPTY_ARRAY;

        try {
            if (m_mutex.attempt(m_timeout)) {
                try {
                    elements = retrieveElements(m_elements, Math.min(size(), numElements));
                } finally {
                    m_mutex.release();
                }
            }
        } catch (InterruptedException ie) {
            //TODO: exception handling
        }

        getDequeueInterceptor().after(this);
        return elements;
    }

    public Object[] dequeueAll() {
        getDequeueInterceptor().before(this);
        Object[] elements = EMPTY_ARRAY;

        try {
            if (m_mutex.attempt(m_timeout)) {
                try {
                    elements = retrieveElements(m_elements, size());
                } finally {
                    m_mutex.release();
                }
            }
        } catch (InterruptedException ie) {
            // TODO: exception hanlding
        }

        getDequeueInterceptor().after(this);
        return elements;
    }

    /**
     * Removes the given number of elements from the given <code>buf</code>
     * and returns them in an array. Trusts the caller to pass in a buffer
     * full of <code>Object</code>s and with at least
     * <code>count</code> elements available.
     * <p>
     * @param buf to remove elements from, the caller is responsible
     *            for synchronizing access
     * @param count number of elements to remove/return
     * @return requested number of elements
     */
    private static Object[] retrieveElements(Buffer buf, int count) {
        Object[] elements = new Object[count];

        for (int i = 0; i < count; i++) {
            elements[i] = buf.remove();
        }

        return elements;
    }

    public Object dequeue() {
        getDequeueInterceptor().before(this);
        Object element = null;

        try {
            if (m_mutex.attempt(m_timeout)) {
                try {
                    if (size() > 0) {
                        element = m_elements.remove();
                    }
                } finally {
                    m_mutex.release();
                }
            }
        } catch (InterruptedException ie) {
            // TODO: exception handling
        }

        getDequeueInterceptor().after(this);
        return element;
    }

    private static final class DefaultPreparedEnqueue implements PreparedEnqueue {
        private final DefaultQueue m_parent;
        private Object[] m_elements;

        private DefaultPreparedEnqueue(DefaultQueue parent, Object[] elements) {
            m_parent = parent;
            m_elements = elements;
            m_parent.m_reserve += elements.length;
        }

        public void commit() {
            if (null == m_elements) {
                throw new IllegalStateException("This PreparedEnqueue has already been processed!");
            }

            try {
                m_parent.m_reserve -= m_elements.length;
                m_parent.enqueue(m_elements);
                m_elements = null;
            } catch (Exception e) {
                throw new IllegalStateException("Default enqueue did not happen--should be impossible");
                // will never happen
            }
        }

        public void abort() {
            if (null == m_elements) {
                throw new IllegalStateException("This PreparedEnqueue has already been processed!");
            }

            m_parent.m_reserve -= m_elements.length;
            m_elements = null;
        }
    }
}