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    package org.apache.commons.pool;
019    
020    import java.util.HashMap;
021    import java.util.Map;
022    import java.util.Iterator;
023    
024    import org.apache.commons.pool.PoolableObjectFactory;
025    import org.apache.commons.pool.KeyedPoolableObjectFactory;
026    
027    /**
028     * Object factory with configurable latencies for object lifecycle methods.
029     * This factory will also track and enforce maxActive, maxActivePerKey contracts.
030     * If the factory's maxActive / maxActivePerKey are set to match those of the
031     * pool, makeObject will throw IllegalStateException if the number of makes - destroys
032     * (per key) exceeds the configured max.
033     *
034     */
035    public class WaiterFactory implements PoolableObjectFactory,
036    KeyedPoolableObjectFactory {
037        
038        /** Latency of activateObject */
039        private final long activateLatency;
040        
041        /** Latency of destroyObject */
042        private final long destroyLatency;
043        
044        /** Latency of makeObject */
045        private final long makeLatency;
046        
047        /** Latency of passivateObject */
048        private final long passivateLatency;
049        
050        /** Latency of validateObject */
051        private final long validateLatency;
052        
053        /** Latency of doWait for Waiter instances created by this factory */
054        private final long waiterLatency;
055        
056        /** Probability that passivation will invalidate Waiter instances */
057        private final double passivateInvalidationProbability;
058        
059        /** Count of (makes - destroys) since last reset */
060        private long activeCount = 0;
061        
062        /** Count of (makes - destroys) per key since last reset */
063        private Map activeCounts = new HashMap();
064        
065        /** Maximum of (makes - destroys) - if exceeded IllegalStateException */
066        private final long maxActive;  // GKOP 1.x calls this maxTotal
067        
068        /** Maximum of (makes - destroys) per key */
069        private final long maxActivePerKey;  // GKOP 1.x calls this maxActive
070    
071        public WaiterFactory(long activateLatency, long destroyLatency,
072                long makeLatency, long passivateLatency, long validateLatency,
073                long waiterLatency,long maxActive, long maxActivePerKey,
074                double passivateInvalidationProbability) {
075            this.activateLatency = activateLatency;
076            this.destroyLatency = destroyLatency;
077            this.makeLatency = makeLatency;
078            this.passivateLatency = passivateLatency;
079            this.validateLatency = validateLatency;
080            this.waiterLatency = waiterLatency;
081            this.maxActive = maxActive;
082            this.maxActivePerKey = maxActivePerKey;
083            this.passivateInvalidationProbability = passivateInvalidationProbability;
084        }
085        
086        public WaiterFactory(long activateLatency, long destroyLatency,
087                long makeLatency, long passivateLatency, long validateLatency,
088                long waiterLatency) {
089            this(activateLatency, destroyLatency, makeLatency, passivateLatency,
090                    validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
091        }
092        
093        public WaiterFactory(long activateLatency, long destroyLatency,
094                long makeLatency, long passivateLatency, long validateLatency,
095                long waiterLatency,long maxActive) {
096            this(activateLatency, destroyLatency, makeLatency, passivateLatency,
097                    validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
098        }
099    
100        public void activateObject(Object obj) throws Exception {
101            doWait(activateLatency);
102            ((Waiter) obj).setActive(true);
103        }
104    
105        public void destroyObject(Object obj) throws Exception {
106            doWait(destroyLatency);
107            ((Waiter) obj).setValid(false);
108            ((Waiter) obj).setActive(false);
109            // Decrement *after* destroy 
110            synchronized (this) {
111                activeCount--;
112            }
113        }
114    
115        public Object makeObject() throws Exception {
116            // Increment and test *before* make
117            synchronized (this) {
118                if (activeCount >= maxActive) {
119                    throw new IllegalStateException("Too many active instances: " +
120                    activeCount + " in circulation with maxActive = " + maxActive);
121                } else {
122                    activeCount++;
123                }
124            }
125            doWait(makeLatency);
126            return new Waiter(false, true, waiterLatency);
127        }
128    
129        public void passivateObject(Object arg0) throws Exception {
130            ((Waiter) arg0).setActive(false);
131            doWait(passivateLatency);
132            if (Math.random() < passivateInvalidationProbability) {
133                ((Waiter) arg0).setValid(false);
134            }
135        }
136    
137        public boolean validateObject(Object arg0) {
138            doWait(validateLatency);
139            return ((Waiter) arg0).isValid();
140        }
141        
142        protected void doWait(long latency) {
143            try {
144                Thread.sleep(latency);
145            } catch (InterruptedException ex) {
146                // ignore
147            }
148        }
149        
150        public synchronized void reset() {
151            activeCount = 0;
152            if (activeCounts.isEmpty()) {
153                return;
154            }
155            Iterator it = activeCounts.keySet().iterator();
156            while (it.hasNext()) {
157                Object key = it.next();
158                activeCounts.put(key, new Integer (0));
159            }
160        }
161    
162        /**
163         * @return the maxActive
164         */
165        public synchronized long getMaxActive() {
166            return maxActive;
167        }
168    
169        // KeyedPoolableObjectFactory methods
170        
171        public void activateObject(Object key, Object obj) throws Exception {
172            activateObject(obj);
173        }
174    
175        public void destroyObject(Object key, Object obj) throws Exception {
176            destroyObject(obj);
177            synchronized (this) {
178                Integer count = (Integer) activeCounts.get(key);
179                activeCounts.put(key, new Integer(count.intValue() - 1));
180            }
181        }
182    
183        public Object makeObject(Object key) throws Exception {
184            synchronized (this) {
185                Integer count = (Integer) activeCounts.get(key);
186                if (count == null) {
187                    count = new Integer(1);
188                    activeCounts.put(key, count);
189                } else {
190                    if (count.intValue() >= maxActivePerKey) {
191                        throw new IllegalStateException("Too many active " +
192                        "instances for key = " + key + ": " + count.intValue() + 
193                        " in circulation " + "with maxActivePerKey = " + 
194                        maxActivePerKey);
195                    } else {
196                        activeCounts.put(key, new Integer(count.intValue() + 1));
197                    }
198                }
199            }
200            return makeObject();
201        }
202    
203        public void passivateObject(Object key, Object obj) throws Exception {
204            passivateObject(obj);
205        }
206    
207        public boolean validateObject(Object key, Object obj) {
208            return validateObject(obj);
209        }
210    
211    }