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.dbcp.datasources;
018:
019: import java.io.Externalizable;
020: import java.io.IOException;
021: import java.io.ObjectInput;
022: import java.io.ObjectOutput;
023: import java.util.Iterator;
024:
025: /**
026: * <p>
027: * This class has been copied from Commons Collections, version 2.1 in order
028: * to eliminate the dependency of dbcp on collections. It has package scope
029: * to prevent its inclusion in the dbcp public API. The class declaration below
030: * should *not* be changed to public.
031: * </p>
032: * <p>
033: * An implementation of a Map which has a maximum size and uses a Least Recently Used
034: * algorithm to remove items from the Map when the maximum size is reached and new items are added.
035: * </p>
036: *
037: * <p>
038: * A synchronized version can be obtained with:
039: * <code>Collections.synchronizedMap( theMapToSynchronize )</code>
040: * If it will be accessed by multiple threads, you _must_ synchronize access
041: * to this Map. Even concurrent get(Object) operations produce indeterminate
042: * behaviour.
043: * </p>
044: *
045: * <p>
046: * Unlike the Collections 1.0 version, this version of LRUMap does use a true
047: * LRU algorithm. The keys for all gets and puts are moved to the front of
048: * the list. LRUMap is now a subclass of SequencedHashMap, and the "LRU"
049: * key is now equivalent to LRUMap.getFirst().
050: * </p>
051: *
052: * @since 1.0
053: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
054: * @author <a href="mailto:morgand@apache.org">Morgan Delagrange</a>
055: */
056: class LRUMap extends SequencedHashMap implements Externalizable {
057:
058: private int maximumSize = 0;
059:
060: /**
061: * Default constructor, primarily for the purpose of
062: * de-externalization. This constructors sets a default
063: * LRU limit of 100 keys, but this value may be overridden
064: * internally as a result of de-externalization.
065: */
066: public LRUMap() {
067: this (100);
068: }
069:
070: /**
071: * Create a new LRUMap with a maximum capacity of <i>i</i>.
072: * Once <i>i</i> capacity is achieved, subsequent gets
073: * and puts will push keys out of the map. See .
074: *
075: * @param i Maximum capacity of the LRUMap
076: */
077: public LRUMap(int i) {
078: super (i);
079: maximumSize = i;
080: }
081:
082: /**
083: * <p>Get the value for a key from the Map. The key
084: * will be promoted to the Most Recently Used position.
085: * Note that get(Object) operations will modify
086: * the underlying Collection. Calling get(Object)
087: * inside of an iteration over keys, values, etc. is
088: * currently unsupported.</p>
089: *
090: * @param key Key to retrieve
091: * @return Returns the value. Returns null if the key has a
092: * null value <i>or</i> if the key has no value.
093: */
094: public Object get(Object key) {
095: if (!containsKey(key))
096: return null;
097:
098: Object value = remove(key);
099: super .put(key, value);
100: return value;
101: }
102:
103: /**
104: * <p>Removes the key and its Object from the Map.</p>
105: *
106: * <p>(Note: this may result in the "Least Recently Used"
107: * object being removed from the Map. In that case,
108: * the removeLRU() method is called. See javadoc for
109: * removeLRU() for more details.)</p>
110: *
111: * @param key Key of the Object to add.
112: * @param value Object to add
113: * @return Former value of the key
114: * @see #removeLRU
115: */
116: public Object put(Object key, Object value) {
117:
118: int mapSize = size();
119: Object retval = null;
120:
121: if (mapSize >= maximumSize) {
122:
123: // don't retire LRU if you are just
124: // updating an existing key
125: if (!containsKey(key)) {
126: // lets retire the least recently used item in the cache
127: removeLRU();
128: }
129: }
130:
131: retval = super .put(key, value);
132:
133: return retval;
134: }
135:
136: /**
137: * This method is used internally by the class for
138: * finding and removing the LRU Object.
139: */
140: protected void removeLRU() {
141: Object key = getFirstKey();
142: // be sure to call super.get(key), or you're likely to
143: // get infinite promotion recursion
144: Object value = super .get(key);
145:
146: remove(key);
147:
148: processRemovedLRU(key, value);
149: }
150:
151: /**
152: * Subclasses of LRUMap may hook into this method to
153: * provide specialized actions whenever an Object is
154: * automatically removed from the cache. By default,
155: * this method does nothing.
156: *
157: * @param key key that was removed
158: * @param value value of that key (can be null)
159: */
160: protected void processRemovedLRU(Object key, Object value) {
161: }
162:
163: // Externalizable interface
164: //-------------------------------------------------------------------------
165: public void readExternal(ObjectInput in) throws IOException,
166: ClassNotFoundException {
167: maximumSize = in.readInt();
168: int size = in.readInt();
169:
170: for (int i = 0; i < size; i++) {
171: Object key = in.readObject();
172: Object value = in.readObject();
173: put(key, value);
174: }
175: }
176:
177: public void writeExternal(ObjectOutput out) throws IOException {
178: out.writeInt(maximumSize);
179: out.writeInt(size());
180: for (Iterator iterator = keySet().iterator(); iterator
181: .hasNext();) {
182: Object key = iterator.next();
183: out.writeObject(key);
184: // be sure to call super.get(key), or you're likely to
185: // get infinite promotion recursion
186: Object value = super .get(key);
187: out.writeObject(value);
188: }
189: }
190:
191: // Properties
192: //-------------------------------------------------------------------------
193: /** Getter for property maximumSize.
194: * @return Value of property maximumSize.
195: */
196: public int getMaximumSize() {
197: return maximumSize;
198: }
199:
200: /** Setter for property maximumSize.
201: * @param maximumSize New value of property maximumSize.
202: */
203: public void setMaximumSize(int maximumSize) {
204: this .maximumSize = maximumSize;
205: while (size() > maximumSize) {
206: removeLRU();
207: }
208: }
209:
210: // add a serial version uid, so that if we change things in the future
211: // without changing the format, we can still deserialize properly.
212: private static final long serialVersionUID = 2197433140769957051L;
213: }
|