Java tutorial
// ======================================================================== // $Id: Pool.java,v 1.13 2005/08/13 00:01:28 gregwilkins Exp $ // Copyright 1999-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package net.lightbody.bmp.proxy.jetty.util; import net.lightbody.bmp.proxy.jetty.log.LogFactory; import org.apache.commons.logging.Log; import java.io.Serializable; import java.util.HashMap; /* ------------------------------------------------------------ */ /** A pool of Objects. * <p> * @version $Id: Pool.java,v 1.13 2005/08/13 00:01:28 gregwilkins Exp $ * @author Juancarlo Aez <juancarlo@modelistica.com> * @author Greg Wilkins <gregw@mortbay.com> */ public class Pool implements LifeCycle, Serializable { private static Log log = LogFactory.getLog(Pool.class); /* ------------------------------------------------------------ */ static int __max = Integer.getInteger("POOL_MAX", 256).intValue(); static int __min = Integer.getInteger("POOL_MIN", 2).intValue(); /* ------------------------------------------------------------ */ public static interface PondLife { int getID(); void enterPool(Pool pool, int id); void poolClosing(); void leavePool(); } /* ------------------------------------------------------------------- */ static HashMap __nameMap = new HashMap(); /* ------------------------------------------------------------------- */ private int _max = __max; private int _min = __min; private String _name; private String _className; private int _maxIdleTimeMs = 10000; private HashMap _attributes = new HashMap(); private transient Class _class; private transient PondLife[] _pondLife; // Array of pondlife indexed by ID. private transient int[] _index; // Mapping of pondlife IDs. Entries with indexes <_available are idle IDs. Entries with indexes>_size are unused IDs. private transient int _size; private transient int _available; private transient int _running = 0; private transient long _lastShrink = 0; // control shrinking to once per maxIdleTime /* ------------------------------------------------------------------- */ public static Pool getPool(String name) { return (Pool) __nameMap.get(name); } /* ------------------------------------------------------------------- */ /* Construct */ public Pool() { } /* ------------------------------------------------------------ */ /** * @return The name of the Pool. */ public String getPoolName() { return _name; } /* ------------------------------------------------------------ */ /** * @param name The pool name * @exception IllegalStateException If the name is already defined. */ public void setPoolName(String name) throws IllegalStateException { synchronized (this) { synchronized (Pool.class) { if (_name != null && !_name.equals(name)) __nameMap.remove(_name); if (__nameMap.containsKey(name)) throw new IllegalStateException("Name already exists"); _name = name; __nameMap.put(_name, this); } } } /* ------------------------------------------------------------ */ /** Set the class. * @param poolClass The class * @exception IllegalStateException If the pool has already * been started. */ public void setPoolClass(Class poolClass) throws IllegalStateException { synchronized (this) { if (_class != poolClass) { if (_running > 0) throw new IllegalStateException("Thread Pool Running"); if (!PondLife.class.isAssignableFrom(poolClass)) throw new IllegalArgumentException("Not PondLife: " + poolClass); _class = poolClass; _className = _class.getName(); } } } /* ------------------------------------------------------------ */ public Class getPoolClass() { return _class; } /* ------------------------------------------------------------ */ public int getMinSize() { return _min; } /* ------------------------------------------------------------ */ public void setMinSize(int min) { _min = min; } /* ------------------------------------------------------------ */ public int getMaxSize() { return _max; } /* ------------------------------------------------------------ */ public void setMaxSize(int max) { _max = max; } /* ------------------------------------------------------------ */ public int getMaxIdleTimeMs() { return _maxIdleTimeMs; } /* ------------------------------------------------------------ */ public void setMaxIdleTimeMs(int maxIdleTimeMs) { _maxIdleTimeMs = maxIdleTimeMs; } /* ------------------------------------------------------------ */ public void setAttribute(String name, Object value) { _attributes.put(name, value); } /* ------------------------------------------------------------ */ public Object getAttribute(String name) { return _attributes.get(name); } /* ------------------------------------------------------------ */ public boolean isStarted() { return _running > 0 && _pondLife != null; } /* ------------------------------------------------------------ */ public int size() { return _size; } /* ------------------------------------------------------------ */ public int available() { return _available; } /* ------------------------------------------------------------ */ public void start() throws Exception { synchronized (this) { _running++; if (_running > 1) return; if (_min >= _max || _max < 1) throw new IllegalStateException("!(0<=min<max)"); // Start the threads _pondLife = new PondLife[_max]; _index = new int[_max]; _size = 0; _available = 0; for (int i = 0; i < _max; i++) _index[i] = i; for (int i = 0; i < _min; i++) newPondLife(); } } /* ------------------------------------------------------------ */ public void stop() throws InterruptedException { synchronized (this) { _running--; if (_running > 0) return; notifyAll(); } if (_pondLife != null && _size > 0) { for (int i = 0; i < _pondLife.length; i++) closePondLife(i); Thread.yield(); for (int i = 0; i < _pondLife.length; i++) stopPondLife(i); } synchronized (this) { _pondLife = null; _index = null; _size = 0; _available = 0; } } /* ------------------------------------------------------------ */ public PondLife get(int timeoutMs) throws Exception { PondLife pl = null; // Defer to other threads before locking if (_available < _min) Thread.yield(); int new_id = -1; // Try to get pondlife without creating new one. synchronized (this) { // Wait if none available. if (_running > 0 && _available == 0 && _size == _pondLife.length && timeoutMs > 0) wait(timeoutMs); // If still running if (_running > 0) { // if pondlife available if (_available > 0) { int id = _index[--_available]; pl = _pondLife[id]; } else if (_size < _pondLife.length) { // Reserve spot for a new one new_id = reservePondLife(false); } } // create reserved pondlife if (pl == null && new_id >= 0) pl = newPondLife(new_id); } return pl; } /* ------------------------------------------------------------ */ public void put(PondLife pl) throws InterruptedException { int id = pl.getID(); synchronized (this) { if (_running == 0) stopPondLife(id); else if (_pondLife[id] != null) { _index[_available++] = id; notify(); } } } /* ------------------------------------------------------------ */ public void shrink() throws InterruptedException { if (_running == 0) return; synchronized (this) { // If we have a maxIdleTime, then only shrink once per period. if (_maxIdleTimeMs > 0) { long now = System.currentTimeMillis(); if ((now - _lastShrink) < _maxIdleTimeMs) return; // don't shrink _lastShrink = now; } // shrink if we are running and have available threads and we are above minimal size if (_running > 0 && _available > 0 && _size > _min) stopPondLife(_index[--_available]); } } /* ------------------------------------------------------------ */ private int reservePondLife(boolean available) throws Exception { int id = -1; synchronized (this) { id = _index[_size++]; if (available) _index[_available++] = id; } return id; } /* ------------------------------------------------------------ */ private PondLife newPondLife(int id) throws Exception { PondLife pl = (PondLife) _class.newInstance(); _pondLife[id] = pl; pl.enterPool(this, id); return pl; } /* ------------------------------------------------------------ */ private PondLife newPondLife() throws Exception { return newPondLife(reservePondLife(true)); } /* ------------------------------------------------------------ */ private void closePondLife(int id) { if (_pondLife[id] != null) _pondLife[id].poolClosing(); } /* ------------------------------------------------------------ */ private void stopPondLife(int id) { PondLife pl = null; synchronized (this) { pl = _pondLife[id]; if (pl != null) { _pondLife[id] = null; _index[--_size] = id; if (_available > _size) _available = _size; } } if (pl != null) pl.leavePool(); } /* ------------------------------------------------------------ */ public void dump(String msg) { StringBuffer pond = new StringBuffer(); StringBuffer avail = new StringBuffer(); StringBuffer index = new StringBuffer(); pond.append("pond: "); avail.append("avail:"); index.append("index:"); for (int i = 0; i < _pondLife.length; i++) { if (_pondLife[i] == null) pond.append(" "); else { pond.append(' '); StringUtil.append(pond, (byte) i, 16); } if (i == _size) avail.append(i == _available ? " AS" : " S"); else avail.append(i == _available ? " A " : " "); index.append(' '); StringUtil.append(index, (byte) _index[i], 16); } System.err.println(); System.err.println(msg); System.err.println(pond); System.err.println(avail); System.err.println(index); } /* ------------------------------------------------------------ */ private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { in.defaultReadObject(); if (_class == null || !_class.getName().equals(_className)) { try { setPoolClass(Loader.loadClass(Pool.class, _className)); } catch (Exception e) { log.warn(LogSupport.EXCEPTION, e); throw new java.io.InvalidObjectException(e.toString()); } } } }