/**
*
* 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.message.util;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.message.Packet;
/**
* MemoryBoundedQueue is a queue bounded by memory usage for Packets
*
* @version $Revision: 1.2 $
*/
public class MemoryBoundedQueue implements BoundedPacketQueue {
private MemoryBoundedQueueManager queueManager;
private String name;
private boolean stopped = false;
private boolean closed = false;
private long memoryUsedByThisQueue;
private Object outLock = new Object();
private Object inLock = new Object();
private LinkedList internalList = new LinkedList();
private static final int WAIT_TIMEOUT = 100;
private static final Log log = LogFactory.getLog(MemoryBoundedQueueManager.class);
/**
* Constructor
*
* @param name
* @param manager
*/
MemoryBoundedQueue(String name, MemoryBoundedQueueManager manager) {
this.name = name;
this.queueManager = manager;
}
/**
* @return the name of this MemoryBoundedQueue
*/
public String getName() {
return name;
}
/**
* @return a pretty print of this queue
*/
public String toString() {
return "" + name + " , cardinality = " + size() + " memory usage = " + memoryUsedByThisQueue;
}
/**
* @return the number of items held by this queue
*/
public int size() {
return internalList.size();
}
/**
* @return an aproximation the memory used by this queue
*/
public long getLocalMemoryUsedByThisQueue() {
return memoryUsedByThisQueue;
}
/**
* close and remove this queue from the MemoryBoundedQueueManager
*/
public void close() {
try {
clear();
closed = true;
synchronized (outLock) {
outLock.notifyAll();
}
synchronized (inLock) {
inLock.notifyAll();
}
}
catch (Throwable e) {
e.printStackTrace();
}
finally {
queueManager.removeMemoryBoundedQueue(getName());
}
}
/**
* Enqueue a Packet without checking memory usage limits
*
* @param packet
*/
public void enqueueNoBlock(Packet packet) {
if (!closed) {
synchronized (outLock) {
internalList.add(packet);
incrementMemoryUsed(packet);
outLock.notify();
}
}
}
/**
* Enqueue a Packet to this queue
*
* @param packet
*/
public void enqueue(Packet packet) {
if (!queueManager.isFull()) {
enqueueNoBlock(packet);
}
else {
synchronized (inLock) {
try {
while (queueManager.isFull() && !closed) {
inLock.wait(WAIT_TIMEOUT);
}
}
catch (InterruptedException ie) {
}
}
enqueueNoBlock(packet);
}
}
/**
* Enqueue a packet to the head of the queue with total disregard for memory constraints
*
* @param packet
*/
public final void enqueueFirstNoBlock(Packet packet) {
if (!closed) {
synchronized (outLock) {
internalList.addFirst(packet);
incrementMemoryUsed(packet);
outLock.notify();
}
}
}
/**
* Enqueue a Packet to the head of the queue
*
* @param packet
* @throws InterruptedException
*/
public void enqueueFirst(Packet packet) throws InterruptedException {
if (!queueManager.isFull()) {
enqueueFirstNoBlock(packet);
}
else {
synchronized (inLock) {
while (queueManager.isFull() && !closed) {
inLock.wait(WAIT_TIMEOUT);
}
}
enqueueFirstNoBlock(packet);
}
}
/**
* @return the first dequeued Packet or blocks until one is available
* @throws InterruptedException
*/
public Packet dequeue() throws InterruptedException {
Packet result = null;
synchronized (outLock) {
while (internalList.isEmpty() && !closed) {
outLock.wait(WAIT_TIMEOUT);
}
result = dequeueNoWait();
}
return result;
}
/**
* Dequeues a Packet from the head of the queue
*
* @param timeInMillis time to wait for a Packet to be available
* @return the first Packet or null if none available within <I>timeInMillis </I>
* @throws InterruptedException
*/
public Packet dequeue(long timeInMillis) throws InterruptedException {
if (timeInMillis == 0) {
return dequeue();
}
else {
synchronized (outLock) {
// if timeInMillis is less than zero assume nowait
if (timeInMillis >= 0) {
if (internalList.isEmpty() && !closed) {
outLock.wait(timeInMillis);
}
}
return dequeueNoWait();
}
}
}
/**
* dequeues a Packet from the head of the queue
*
* @return the Packet at the head of the queue or null, if none is available
* @throws InterruptedException
*/
public Packet dequeueNoWait() throws InterruptedException {
Packet packet = null;
if (stopped) {
synchronized (outLock) {
while (stopped && !closed) {
outLock.wait(WAIT_TIMEOUT);
}
}
}
synchronized (outLock) {
if (!internalList.isEmpty()) {
packet = (Packet) internalList.removeFirst();
decrementMemoryUsed(packet);
}
}
return packet;
}
/**
* @return true if the queue is enabled for dequeing (default = true)
*/
public boolean isStarted() {
return stopped == false;
}
/**
* disable dequeueing
*/
public void stop() {
synchronized (outLock) {
stopped = true;
}
}
/**
* enable dequeueing
*/
public void start() {
stopped = false;
synchronized (outLock) {
outLock.notifyAll();
}
synchronized (inLock) {
inLock.notifyAll();
}
}
/**
* Remove a packet from the queue
*
* @param packet
* @return true if the packet was found
*/
public boolean remove(Packet packet) {
boolean result = false;
synchronized (inLock) {
if (!internalList.isEmpty()) {
result = internalList.remove(packet);
}
if (result) {
decrementMemoryUsed(packet);
if (!queueManager.isFull()) {
inLock.notify();
}
}
}
return result;
}
/**
* remove any Packets in the queue
*/
public void clear() {
synchronized (inLock) {
while (!internalList.isEmpty()) {
Packet packet = (Packet) internalList.removeFirst();
decrementMemoryUsed(packet);
}
inLock.notify();
}
}
/**
* @return true if the queue is empty
*/
public boolean isEmpty() {
return internalList.isEmpty();
}
/**
* retrieve a Packet at an indexed position in the queue
*
* @param index
* @return
*/
public Packet get(int index) {
return (Packet) internalList.get(index);
}
/**
* Retrieve a shallow copy of the contents as a list
*
* @return a list containing the bounded queue contents
*/
public List getContents() {
return (List) internalList.clone();
}
private void incrementMemoryUsed(Packet packet) {
if (packet != null) {
memoryUsedByThisQueue += queueManager.incrementMemoryUsed(packet);
}
}
private void decrementMemoryUsed(Packet packet) {
if (packet != null) {
memoryUsedByThisQueue -= queueManager.decrementMemoryUsed(packet);
}
}
}