001: /*
002: * Copyright 2005-2006 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.collections.buffer;
017:
018: import java.io.PrintWriter;
019: import java.io.StringWriter;
020: import java.util.Collection;
021: import java.util.Iterator;
022:
023: import org.apache.commons.collections.BoundedCollection;
024: import org.apache.commons.collections.Buffer;
025: import org.apache.commons.collections.BufferOverflowException;
026: import org.apache.commons.collections.BufferUnderflowException;
027: import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
028:
029: /**
030: * Decorates another <code>Buffer</code> to ensure a fixed maximum size.
031: * <p>
032: * Note: This class should only be used if you need to add bounded
033: * behaviour to another buffer. If you just want a bounded buffer then
034: * you should use {@link BoundedFifoBuffer} or {@link CircularFifoBuffer}.
035: * <p>
036: * The decoration methods allow you to specify a timeout value.
037: * This alters the behaviour of the add methods when the buffer is full.
038: * Normally, when the buffer is full, the add method will throw an exception.
039: * With a timeout, the add methods will wait for up to the timeout period
040: * to try and add the elements.
041: *
042: * @author James Carman
043: * @author Stephen Colebourne
044: * @version $Revision: 405927 $ $Date: 2006-05-12 23:57:03 +0100 (Fri, 12 May 2006) $
045: * @since Commons Collections 3.2
046: */
047: public class BoundedBuffer extends SynchronizedBuffer implements
048: BoundedCollection {
049:
050: /** The serialization version. */
051: private static final long serialVersionUID = 1536432911093974264L;
052:
053: /** The maximum size. */
054: private final int maximumSize;
055: /** The timeout milliseconds. */
056: private final long timeout;
057:
058: /**
059: * Factory method to create a bounded buffer.
060: * <p>
061: * When the buffer is full, it will immediately throw a
062: * <code>BufferOverflowException</code> on calling <code>add()</code>.
063: *
064: * @param buffer the buffer to decorate, must not be null
065: * @param maximumSize the maximum size, must be size one or greater
066: * @return a new bounded buffer
067: * @throws IllegalArgumentException if the buffer is null
068: * @throws IllegalArgumentException if the maximum size is zero or less
069: */
070: public static BoundedBuffer decorate(Buffer buffer, int maximumSize) {
071: return new BoundedBuffer(buffer, maximumSize, 0L);
072: }
073:
074: /**
075: * Factory method to create a bounded buffer that blocks for a maximum
076: * amount of time.
077: *
078: * @param buffer the buffer to decorate, must not be null
079: * @param maximumSize the maximum size, must be size one or greater
080: * @param timeout the maximum amount of time to wait in milliseconds
081: * @return a new bounded buffer
082: * @throws IllegalArgumentException if the buffer is null
083: * @throws IllegalArgumentException if the maximum size is zero or less
084: */
085: public static BoundedBuffer decorate(Buffer buffer,
086: int maximumSize, long timeout) {
087: return new BoundedBuffer(buffer, maximumSize, timeout);
088: }
089:
090: //-----------------------------------------------------------------------
091: /**
092: * Constructor that wraps (not copies) another buffer, making it bounded
093: * waiting only up to a maximum amount of time.
094: *
095: * @param buffer the buffer to wrap, must not be null
096: * @param maximumSize the maximum size, must be size one or greater
097: * @param timeout the maximum amount of time to wait
098: * @throws IllegalArgumentException if the buffer is null
099: * @throws IllegalArgumentException if the maximum size is zero or less
100: */
101: protected BoundedBuffer(Buffer buffer, int maximumSize, long timeout) {
102: super (buffer);
103: if (maximumSize < 1) {
104: throw new IllegalArgumentException();
105: }
106: this .maximumSize = maximumSize;
107: this .timeout = timeout;
108: }
109:
110: //-----------------------------------------------------------------------
111: public Object remove() {
112: synchronized (lock) {
113: Object returnValue = getBuffer().remove();
114: lock.notifyAll();
115: return returnValue;
116: }
117: }
118:
119: public boolean add(Object o) {
120: synchronized (lock) {
121: timeoutWait(1);
122: return getBuffer().add(o);
123: }
124: }
125:
126: public boolean addAll(final Collection c) {
127: synchronized (lock) {
128: timeoutWait(c.size());
129: return getBuffer().addAll(c);
130: }
131: }
132:
133: public Iterator iterator() {
134: return new NotifyingIterator(collection.iterator());
135: }
136:
137: private void timeoutWait(final int nAdditions) {
138: // method synchronized by callers
139: if (nAdditions > maximumSize) {
140: throw new BufferOverflowException(
141: "Buffer size cannot exceed " + maximumSize);
142: }
143: if (timeout <= 0) {
144: // no wait period (immediate timeout)
145: if (getBuffer().size() + nAdditions > maximumSize) {
146: throw new BufferOverflowException(
147: "Buffer size cannot exceed " + maximumSize);
148: }
149: return;
150: }
151: final long expiration = System.currentTimeMillis() + timeout;
152: long timeLeft = expiration - System.currentTimeMillis();
153: while (timeLeft > 0
154: && getBuffer().size() + nAdditions > maximumSize) {
155: try {
156: lock.wait(timeLeft);
157: timeLeft = expiration - System.currentTimeMillis();
158: } catch (InterruptedException ex) {
159: PrintWriter out = new PrintWriter(new StringWriter());
160: ex.printStackTrace(out);
161: throw new BufferUnderflowException(
162: "Caused by InterruptedException: "
163: + out.toString());
164: }
165: }
166: if (getBuffer().size() + nAdditions > maximumSize) {
167: throw new BufferOverflowException("Timeout expired");
168: }
169: }
170:
171: public boolean isFull() {
172: // size() is synchronized
173: return (size() == maxSize());
174: }
175:
176: public int maxSize() {
177: return maximumSize;
178: }
179:
180: //-----------------------------------------------------------------------
181: /**
182: * BoundedBuffer iterator.
183: */
184: private class NotifyingIterator extends AbstractIteratorDecorator {
185:
186: public NotifyingIterator(Iterator it) {
187: super (it);
188: }
189:
190: public void remove() {
191: synchronized (lock) {
192: iterator.remove();
193: lock.notifyAll();
194: }
195: }
196: }
197: }
|