/**
*
* 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.ArrayList;
import java.util.List;
import javax.jms.JMSException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.message.Packet;
import org.codehaus.activemq.service.QueueListEntry;
import org.codehaus.activemq.service.impl.DefaultQueueList;
/**
* MemoryBoundedQueue is a queue bounded by memory usage for Packets
*
* @version $Revision: 1.6 $
*/
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 DefaultQueueList internalList = new DefaultQueueList();
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) {
internalList.add(packet);
incrementMemoryUsed(packet);
synchronized (outLock) {
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) {
internalList.addFirst(packet);
incrementMemoryUsed(packet);
synchronized (outLock) {
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 {
Packet result = null;
if (timeInMillis == 0) {
result = dequeue();
}
else {
synchronized (outLock) {
// if timeInMillis is less than zero assume nowait
long waitTime = timeInMillis;
long start = (timeInMillis <= 0) ? 0 : System.currentTimeMillis();
while (!closed) {
result = dequeueNoWait();
if (result != null || waitTime <= 0) {
break;
}
else {
outLock.wait(waitTime);
waitTime = timeInMillis - (System.currentTimeMillis() - start);
}
}
}
}
return result;
}
/**
* 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);
}
}
}
packet = (Packet) internalList.removeFirst();
decrementMemoryUsed(packet);
if (packet != null) {
synchronized (inLock) {
inLock.notify();
}
}
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;
if (!internalList.isEmpty()) {
result = internalList.remove(packet);
}
if (result) {
decrementMemoryUsed(packet);
}
synchronized (inLock) {
inLock.notify();
}
return result;
}
/**
* Remove a Packet by it's id
*
* @param id
* @return
*/
public Packet remove(String id) {
Packet result = null;
QueueListEntry entry = internalList.getFirstEntry();
try {
while (entry != null) {
Packet p = (Packet) entry.getElement();
if (p.getId().equals(id)) {
result = p;
internalList.remove(entry);
break;
}
entry = internalList.getNextEntry(entry);
}
}
catch (JMSException jmsEx) {
jmsEx.printStackTrace();
}
synchronized (inLock) {
inLock.notify();
}
return result;
}
/**
* remove any Packets in the queue
*/
public void clear() {
while (!internalList.isEmpty()) {
Packet packet = (Packet) internalList.removeFirst();
decrementMemoryUsed(packet);
}
synchronized (inLock) {
inLock.notifyAll();
}
}
/**
* @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() {
Object[] array = internalList.toArray();
List list = new ArrayList();
for (int i = 0;i < array.length;i++) {
list.add(array[i]);
}
return list;
}
private synchronized void incrementMemoryUsed(Packet packet) {
if (packet != null) {
memoryUsedByThisQueue += queueManager.incrementMemoryUsed(packet);
}
}
private synchronized void decrementMemoryUsed(Packet packet) {
if (packet != null) {
memoryUsedByThisQueue -= queueManager.decrementMemoryUsed(packet);
}
}
}