1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  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 // Contributors:     Mathias Bogaert
19 //                   joelr@viair.com
20 
21 package org.apache.log4j.helpers;
22 
23 import org.apache.log4j.spi.LoggingEvent;
24 
25 /**
26    <code>BoundedFIFO</code> serves as the bounded first-in-first-out
27    buffer heavily used by the {@link org.apache.log4j.AsyncAppender}.
28 
29    @author Ceki G&uuml;lc&uuml;
30    @since version 0.9.1 */
31 public class BoundedFIFO {
32 
33   LoggingEvent[] buf;
34   int numElements = 0;
35   int first = 0;
36   int next = 0;
37   int maxSize;
38 
39   /**
40      Instantiate a new BoundedFIFO with a maximum size passed as argument.
41    */
42   public
BoundedFIFO(int maxSize)43   BoundedFIFO(int maxSize) {
44    if(maxSize < 1) {
45       throw new IllegalArgumentException("The maxSize argument ("+maxSize+
46 			    ") is not a positive integer.");
47     }
48     this.maxSize = maxSize;
49     buf = new LoggingEvent[maxSize];
50   }
51 
52   /**
53      Get the first element in the buffer. Returns <code>null</code> if
54      there are no elements in the buffer.  */
55   public
get()56   LoggingEvent get() {
57     if(numElements == 0)
58       return null;
59 
60     LoggingEvent r = buf[first];
61     buf[first] = null; // help garbage collection
62 
63     if(++first == maxSize) {
64 	first = 0;
65     }
66     numElements--;
67     return r;
68   }
69 
70   /**
71      Place a {@link LoggingEvent} in the buffer. If the buffer is full
72      then the event is <b>silently dropped</b>. It is the caller's
73      responsability to make sure that the buffer has free space.  */
74   public
put(LoggingEvent o)75   void put(LoggingEvent o) {
76     if(numElements != maxSize) {
77       buf[next] = o;
78       if(++next == maxSize) {
79 	next = 0;
80       }
81       numElements++;
82     }
83   }
84 
85   /**
86      Get the maximum size of the buffer.
87    */
88   public
getMaxSize()89   int getMaxSize() {
90     return maxSize;
91   }
92 
93   /**
94      Return <code>true</code> if the buffer is full, that is, whether
95      the number of elements in the buffer equals the buffer size. */
96   public
isFull()97   boolean isFull() {
98     return numElements == maxSize;
99   }
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
length()107   int length() {
108     return numElements;
109   }
110 
111 
min(int a, int b)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
resize(int newSize)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
wasEmpty()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
wasFull()177   boolean wasFull() {
178     return (numElements+1 == maxSize);
179   }
180 
181 }
182