1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package com.gargoylesoftware.base.trace;
39
40 import com.gargoylesoftware.base.util.DetailedIllegalArgumentException;
41 import com.gargoylesoftware.base.util.DetailedNullPointerException;
42 import java.io.Serializable;
43
44 /***
45 * <p style="color: orange">Internal use only.</p>.
46 * <p>A circular queue with blocking semantics.</p>
47 *
48 * @version $Revision: 1.8 $
49 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
50 */
51 public class BlockingCircularQueue
52 implements
53 Serializable {
54
55
56 private static final long serialVersionUID = 2206052328240171051L;
57
58
59 private final Object getLock_ = new Object();
60 private final Object addLock_ = new Object();
61
62
63 private final Object queue_[];
64
65 private final int capacity_;
66 private int size_;
67
68
69 private int front_;
70 private int back_;
71
72
73 /***
74 * Create the queue with a capacity of 20
75 */
76 public BlockingCircularQueue() {
77 this( 20 );
78 }
79
80
81
82 /***
83 * Create the queue with the specified capacity
84 *
85 *@param capacity The size of the queue
86 */
87 public BlockingCircularQueue( final int capacity ) {
88 queue_ = new Object[capacity];
89 capacity_ = capacity;
90 if( capacity < 2 ) {
91 throw new DetailedIllegalArgumentException( "capacity", capacity, "Must be larger than two" );
92 }
93 }
94
95
96 /***
97 * Remove all items from the queue
98 */
99 public synchronized void clear() {
100 front_ = 0;
101 back_ = 0;
102 size_ = 0;
103
104
105
106 int i;
107 for( i = 0; i < capacity_; i++ ) {
108 queue_[i] = null;
109 }
110 }
111
112
113 /***
114 * Add an object to the queue. If the collection is currently full then
115 * block until an object is removed.
116 *
117 * @param object The object to add.
118 * @return true if the object was successfully added.
119 */
120 public boolean add( final Object object ) {
121 assertNotNull( "object", object );
122 while( true ) {
123 synchronized( this ) {
124 if( size_ != capacity_ ) {
125 if( size_ == 0 ) {
126 front_ = 0;
127 back_ = 0;
128 }
129 else {
130 front_ = incrementPosition( front_ );
131 }
132 queue_[front_] = object;
133 synchronized( getLock_ ) {
134 getLock_.notify();
135 }
136 size_++;
137 return true;
138 }
139 }
140 synchronized( addLock_ ) {
141 try {
142 addLock_.wait();
143 }
144 catch( final InterruptedException e ) {
145
146 }
147 }
148 }
149 }
150
151
152 /***
153 * Return the next item in the queue. The returned item is removed from the
154 * collection. If no objects are present then block until an object has
155 * been added.
156 *
157 * @return The next item.
158 */
159 public Object next() {
160 while( true ) {
161
162 synchronized( this ) {
163 if( isEmpty() == false ) {
164 final Object object = queue_[back_];
165 queue_[back_] = null;
166 back_ = incrementPosition( back_ );
167 synchronized( addLock_ ) {
168 addLock_.notify();
169 }
170 size_--;
171 return object;
172 }
173 }
174
175 synchronized( getLock_ ) {
176 try {
177 getLock_.wait();
178 }
179 catch( InterruptedException e ) {
180
181 }
182 }
183 }
184 }
185
186
187 /***
188 * Utility method to increment the position and roll back to zero once we
189 * hit the end. Return the new position after the adjustment.
190 * @param position The position to increment
191 * @return the new position
192 */
193 private int incrementPosition( final int position ) {
194 if( ( position < 0 ) || ( position >= capacity_ ) ) {
195 throw new DetailedIllegalArgumentException(
196 "position", new Integer( position ), "Must be within 1 and " + capacity_ );
197 }
198 int newPosition = position + 1;
199 if( newPosition == capacity_ ) {
200 newPosition = 0;
201 }
202 return newPosition;
203 }
204
205
206 /***
207 * Return the number of objects currently in the collection.
208 * @return The object count.
209 */
210 public int size() {
211 return size_;
212 }
213
214
215 /***
216 * Return true if the collection is empty.
217 * @return true if the collection is empty.
218 */
219 public boolean isEmpty() {
220 return size_ == 0;
221 }
222
223
224 /***
225 * Return a string representation of this object.
226 * @return a string representation of this object.
227 */
228 public String toString() {
229 final StringBuffer buffer = new StringBuffer();
230 buffer.append( getClass().getName() );
231 buffer.append( ": capacity_=" );
232 buffer.append( capacity_ );
233 buffer.append( " size_=" );
234 buffer.append( size_ );
235 buffer.append( " front_=" );
236 buffer.append( front_ );
237 buffer.append( " back_=" );
238 buffer.append( back_ );
239 return buffer.toString();
240 }
241
242
243 /***
244 * Verify that the specified value is not null. If it is then throw an
245 * exception
246 *
247 *@param fieldName The name of the field to check
248 *@param fieldValue The value of the field to check
249 *@exception DetailedNullPointerException If fieldValue is null
250 */
251 protected final void assertNotNull( final String fieldName, final Object fieldValue )
252 throws DetailedNullPointerException {
253 if( fieldValue == null ) {
254 throw new DetailedNullPointerException( fieldName );
255 }
256 }
257 }
258