// ======================================================================== // $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 org.browsermob.proxy.jetty.util; import org.apache.commons.logging.Log; import org.browsermob.proxy.jetty.log.LogFactory; 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 AƱez <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()); } } } }