1 /***
2 *
3 * Copyright 2004 Protique Ltd
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 **/
18
19 package org.codehaus.activemq.message.util;
20 import java.util.LinkedList;
21 import java.util.List;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.codehaus.activemq.message.Packet;
25
26 /***
27 * MemoryBoundedQueue is a queue bounded by memory usage for Packets
28 *
29 * @version $Revision: 1.2 $
30 */
31 public class MemoryBoundedQueue implements BoundedPacketQueue {
32 private MemoryBoundedQueueManager queueManager;
33 private String name;
34 private boolean stopped = false;
35 private boolean closed = false;
36 private long memoryUsedByThisQueue;
37 private Object outLock = new Object();
38 private Object inLock = new Object();
39 private LinkedList internalList = new LinkedList();
40 private static final int WAIT_TIMEOUT = 100;
41 private static final Log log = LogFactory.getLog(MemoryBoundedQueueManager.class);
42
43 /***
44 * Constructor
45 *
46 * @param name
47 * @param manager
48 */
49 MemoryBoundedQueue(String name, MemoryBoundedQueueManager manager) {
50 this.name = name;
51 this.queueManager = manager;
52 }
53
54 /***
55 * @return the name of this MemoryBoundedQueue
56 */
57 public String getName() {
58 return name;
59 }
60
61 /***
62 * @return a pretty print of this queue
63 */
64 public String toString() {
65 return "" + name + " , cardinality = " + size() + " memory usage = " + memoryUsedByThisQueue;
66 }
67
68 /***
69 * @return the number of items held by this queue
70 */
71 public int size() {
72 return internalList.size();
73 }
74
75 /***
76 * @return an aproximation the memory used by this queue
77 */
78 public long getLocalMemoryUsedByThisQueue() {
79 return memoryUsedByThisQueue;
80 }
81
82 /***
83 * close and remove this queue from the MemoryBoundedQueueManager
84 */
85 public void close() {
86 try {
87 clear();
88 closed = true;
89 synchronized (outLock) {
90 outLock.notifyAll();
91 }
92 synchronized (inLock) {
93 inLock.notifyAll();
94 }
95 }
96 catch (Throwable e) {
97 e.printStackTrace();
98 }
99 finally {
100 queueManager.removeMemoryBoundedQueue(getName());
101 }
102 }
103
104 /***
105 * Enqueue a Packet without checking memory usage limits
106 *
107 * @param packet
108 */
109 public void enqueueNoBlock(Packet packet) {
110 if (!closed) {
111 synchronized (outLock) {
112 internalList.add(packet);
113 incrementMemoryUsed(packet);
114 outLock.notify();
115 }
116 }
117 }
118
119 /***
120 * Enqueue a Packet to this queue
121 *
122 * @param packet
123 */
124 public void enqueue(Packet packet) {
125 if (!queueManager.isFull()) {
126 enqueueNoBlock(packet);
127 }
128 else {
129 synchronized (inLock) {
130 try {
131 while (queueManager.isFull() && !closed) {
132 inLock.wait(WAIT_TIMEOUT);
133 }
134 }
135 catch (InterruptedException ie) {
136 }
137 }
138 enqueueNoBlock(packet);
139 }
140 }
141
142 /***
143 * Enqueue a packet to the head of the queue with total disregard for memory constraints
144 *
145 * @param packet
146 */
147 public final void enqueueFirstNoBlock(Packet packet) {
148 if (!closed) {
149 synchronized (outLock) {
150 internalList.addFirst(packet);
151 incrementMemoryUsed(packet);
152 outLock.notify();
153 }
154 }
155 }
156
157 /***
158 * Enqueue a Packet to the head of the queue
159 *
160 * @param packet
161 * @throws InterruptedException
162 */
163 public void enqueueFirst(Packet packet) throws InterruptedException {
164 if (!queueManager.isFull()) {
165 enqueueFirstNoBlock(packet);
166 }
167 else {
168 synchronized (inLock) {
169 while (queueManager.isFull() && !closed) {
170 inLock.wait(WAIT_TIMEOUT);
171 }
172 }
173 enqueueFirstNoBlock(packet);
174 }
175 }
176
177 /***
178 * @return the first dequeued Packet or blocks until one is available
179 * @throws InterruptedException
180 */
181 public Packet dequeue() throws InterruptedException {
182 Packet result = null;
183 synchronized (outLock) {
184 while (internalList.isEmpty() && !closed) {
185 outLock.wait(WAIT_TIMEOUT);
186 }
187 result = dequeueNoWait();
188 }
189 return result;
190 }
191
192 /***
193 * Dequeues a Packet from the head of the queue
194 *
195 * @param timeInMillis time to wait for a Packet to be available
196 * @return the first Packet or null if none available within <I>timeInMillis </I>
197 * @throws InterruptedException
198 */
199 public Packet dequeue(long timeInMillis) throws InterruptedException {
200 if (timeInMillis == 0) {
201 return dequeue();
202 }
203 else {
204 synchronized (outLock) {
205
206 if (timeInMillis >= 0) {
207 if (internalList.isEmpty() && !closed) {
208 outLock.wait(timeInMillis);
209 }
210 }
211 return dequeueNoWait();
212 }
213 }
214 }
215
216 /***
217 * dequeues a Packet from the head of the queue
218 *
219 * @return the Packet at the head of the queue or null, if none is available
220 * @throws InterruptedException
221 */
222 public Packet dequeueNoWait() throws InterruptedException {
223 Packet packet = null;
224 if (stopped) {
225 synchronized (outLock) {
226 while (stopped && !closed) {
227 outLock.wait(WAIT_TIMEOUT);
228 }
229 }
230 }
231 synchronized (outLock) {
232 if (!internalList.isEmpty()) {
233 packet = (Packet) internalList.removeFirst();
234 decrementMemoryUsed(packet);
235 }
236 }
237 return packet;
238 }
239
240 /***
241 * @return true if the queue is enabled for dequeing (default = true)
242 */
243 public boolean isStarted() {
244 return stopped == false;
245 }
246
247 /***
248 * disable dequeueing
249 */
250 public void stop() {
251 synchronized (outLock) {
252 stopped = true;
253 }
254 }
255
256 /***
257 * enable dequeueing
258 */
259 public void start() {
260 stopped = false;
261 synchronized (outLock) {
262 outLock.notifyAll();
263 }
264 synchronized (inLock) {
265 inLock.notifyAll();
266 }
267 }
268
269 /***
270 * Remove a packet from the queue
271 *
272 * @param packet
273 * @return true if the packet was found
274 */
275 public boolean remove(Packet packet) {
276 boolean result = false;
277 synchronized (inLock) {
278 if (!internalList.isEmpty()) {
279 result = internalList.remove(packet);
280 }
281 if (result) {
282 decrementMemoryUsed(packet);
283 if (!queueManager.isFull()) {
284 inLock.notify();
285 }
286 }
287 }
288 return result;
289 }
290
291 /***
292 * remove any Packets in the queue
293 */
294 public void clear() {
295 synchronized (inLock) {
296 while (!internalList.isEmpty()) {
297 Packet packet = (Packet) internalList.removeFirst();
298 decrementMemoryUsed(packet);
299 }
300 inLock.notify();
301 }
302 }
303
304 /***
305 * @return true if the queue is empty
306 */
307 public boolean isEmpty() {
308 return internalList.isEmpty();
309 }
310
311 /***
312 * retrieve a Packet at an indexed position in the queue
313 *
314 * @param index
315 * @return
316 */
317 public Packet get(int index) {
318 return (Packet) internalList.get(index);
319 }
320
321 /***
322 * Retrieve a shallow copy of the contents as a list
323 *
324 * @return a list containing the bounded queue contents
325 */
326 public List getContents() {
327 return (List) internalList.clone();
328 }
329
330 private void incrementMemoryUsed(Packet packet) {
331 if (packet != null) {
332 memoryUsedByThisQueue += queueManager.incrementMemoryUsed(packet);
333 }
334 }
335
336 private void decrementMemoryUsed(Packet packet) {
337 if (packet != null) {
338 memoryUsedByThisQueue -= queueManager.decrementMemoryUsed(packet);
339 }
340 }
341 }