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
018// Contributors:     Mathias Bogaert
019//                   joelr@viair.com
020
021package org.apache.log4j.helpers;
022
023import org.apache.log4j.spi.LoggingEvent;
024
025/**
026   <code>BoundedFIFO</code> serves as the bounded first-in-first-out
027   buffer heavily used by the {@link org.apache.log4j.AsyncAppender}.
028   
029   @author Ceki G&uuml;lc&uuml; 
030   @since version 0.9.1 */
031public class BoundedFIFO {
032  
033  LoggingEvent[] buf;
034  int numElements = 0;
035  int first = 0;
036  int next = 0;
037  int maxSize;
038
039  /**
040     Instantiate a new BoundedFIFO with a maximum size passed as argument.
041   */
042  public
043  BoundedFIFO(int maxSize) {
044   if(maxSize < 1) {
045      throw new IllegalArgumentException("The maxSize argument ("+maxSize+
046                            ") is not a positive integer.");
047    }
048    this.maxSize = maxSize;
049    buf = new LoggingEvent[maxSize];
050  }
051  
052  /**
053     Get the first element in the buffer. Returns <code>null</code> if
054     there are no elements in the buffer.  */
055  public
056  LoggingEvent get() {
057    if(numElements == 0) 
058      return null;
059    
060    LoggingEvent r = buf[first];
061    buf[first] = null; // help garbage collection
062
063    if(++first == maxSize) {
064        first = 0;
065    }
066    numElements--;    
067    return r;    
068  }
069
070  /**
071     Place a {@link LoggingEvent} in the buffer. If the buffer is full
072     then the event is <b>silently dropped</b>. It is the caller's
073     responsability to make sure that the buffer has free space.  */
074  public 
075  void put(LoggingEvent o) {
076    if(numElements != maxSize) {      
077      buf[next] = o;    
078      if(++next == maxSize) {
079        next = 0;
080      }
081      numElements++;
082    }
083  }
084
085  /**
086     Get the maximum size of the buffer.
087   */
088  public 
089  int getMaxSize() {
090    return maxSize;
091  }
092
093  /**
094     Return <code>true</code> if the buffer is full, that is, whether
095     the number of elements in the buffer equals the buffer size. */
096  public 
097  boolean isFull() {
098    return numElements == maxSize;
099  }
100
101  /**
102     Get the number of elements in the buffer. This number is
103     guaranteed to be in the range 0 to <code>maxSize</code>
104     (inclusive).
105  */
106  public
107  int length() {
108    return numElements;
109  } 
110
111
112  int min(int a, int b) {
113    return a < b ? a : b;
114  }
115
116
117  /**
118     Resize the buffer to a new size. If the new size is smaller than
119     the old size events might be lost.
120     
121     @since 1.1
122   */
123  synchronized
124  public 
125  void resize(int newSize) {
126    if(newSize == maxSize) 
127      return;
128
129
130   LoggingEvent[] tmp = new LoggingEvent[newSize];
131
132   // we should not copy beyond the buf array
133   int len1 = maxSize - first;
134
135   // we should not copy beyond the tmp array
136   len1 = min(len1, newSize);
137
138   // er.. how much do we actually need to copy?
139   // We should not copy more than the actual number of elements.
140   len1 = min(len1, numElements);
141
142   // Copy from buf starting a first, to tmp, starting at position 0, len1 elements.
143   System.arraycopy(buf, first, tmp, 0, len1);
144   
145   // Are there any uncopied elements and is there still space in the new array?
146   int len2 = 0;
147   if((len1 < numElements) && (len1 < newSize)) {
148     len2 = numElements - len1;
149     len2 = min(len2, newSize - len1);
150     System.arraycopy(buf, 0, tmp, len1, len2);
151   }
152   
153   this.buf = tmp;
154   this.maxSize = newSize;    
155   this.first=0;   
156   this.numElements = len1+len2;
157   this.next = this.numElements;
158   if(this.next == this.maxSize) // this should never happen, but again, it just might.
159     this.next = 0;
160  }
161
162  
163  /**
164     Returns <code>true</code> if there is just one element in the
165     buffer. In other words, if there were no elements before the last
166     {@link #put} operation completed.  */
167  public
168  boolean wasEmpty() {
169    return numElements == 1;
170  }
171
172  /**
173      Returns <code>true</code> if the number of elements in the
174      buffer plus 1 equals the maximum buffer size, returns
175      <code>false</code> otherwise. */
176  public
177  boolean wasFull() {
178    return (numElements+1 == maxSize);
179  }
180
181}