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