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