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.logging.Logger;
021    import org.apache.commons.dbcp.AbandonedConfig;
022    import org.apache.commons.dbcp.AbandonedObjectPool;
023    import org.apache.commons.pool.impl.GenericObjectPool;
024    import org.apache.commons.pool.impl.StackObjectPool;
025    import org.apache.commons.pool.impl.SoftReferenceObjectPool;
026    import org.apache.commons.pool.impl.GenericKeyedObjectPool;
027    import org.apache.commons.pool.impl.StackKeyedObjectPool;
028    import org.apache.commons.performance.ConfigurationException;
029    import org.apache.commons.performance.ClientThread;
030    import org.apache.commons.performance.LoadGenerator;
031    import org.apache.commons.performance.Statistics;
032     
033    /**
034     * Configurable load / performance tester for commons pool.
035     * Uses Commons Digester to parse and load configuration and spawns
036     * PoolClientThread instances to generate load and gather statistics.
037     *
038     */
039    public class PoolSoak extends LoadGenerator {
040        
041        // Pool instances
042        private GenericObjectPool genericObjectPool;
043        private GenericKeyedObjectPool genericKeyedObjectPool;
044        private StackObjectPool stackObjectPool;
045        private SoftReferenceObjectPool softReferenceObjectPool;
046        private StackKeyedObjectPool stackKeyedObjectPool;
047        
048        // Pool properties
049        private String poolType;
050        private int maxActive;       // maxActive for GOP, maxTotal for GKOP
051        private int maxActivePerKey; // maxActive for GKOP
052        private int maxIdle;
053        private int minIdle;
054        private long maxWait;
055        private byte exhaustedAction;
056        private boolean testOnBorrow;
057        private boolean testOnReturn;
058        private long timeBetweenEvictions;
059        private int testsPerEviction;
060        private long idleTimeout;
061        private boolean testWhileIdle;
062        private AbandonedConfig abandonedConfig = new AbandonedConfig();
063        private boolean lifo;
064        private double samplingRate;
065        
066        // WaiterFactory properties
067        private long activateLatency;
068        private long destroyLatency;
069        private long makeLatency;
070        private long passivateLatency;
071        private long validateLatency;
072        private long waiterLatency;
073        
074        /**
075         * Add pool configuration to parameters loaded by super.
076         * Also set config file name.
077         */
078        protected void configure() throws Exception {
079            super.configure();
080            digester.addCallMethod("configuration/factory", 
081                    "configureFactory", 6);
082            digester.addCallParam(
083                    "configuration/factory/activate-latency", 0);
084            digester.addCallParam(
085                    "configuration/factory/destroy-latency", 1);
086            digester.addCallParam(
087                    "configuration/factory/make-latency", 2);
088            digester.addCallParam(
089                    "configuration/factory/passivate-latency", 3);
090            digester.addCallParam(
091                    "configuration/factory/validate-latency", 4);
092            digester.addCallParam(
093                    "configuration/factory/waiter-latency", 5);   
094            digester.addCallMethod("configuration/pool", 
095                    "configurePool", 15);
096            digester.addCallParam(
097                    "configuration/pool/max-active", 0);
098            digester.addCallParam(
099                    "configuration/pool/max-active-per-key", 1);
100            digester.addCallParam(
101                    "configuration/pool/max-idle", 2);
102            digester.addCallParam(
103                    "configuration/pool/min-idle", 3);
104            digester.addCallParam(
105                    "configuration/pool/max-wait", 4);
106            digester.addCallParam(
107                    "configuration/pool/exhausted-action", 5);
108            digester.addCallParam(
109                    "configuration/pool/test-on-borrow", 6);
110            digester.addCallParam(
111                    "configuration/pool/test-on-return", 7);
112            digester.addCallParam(
113                    "configuration/pool/time-between-evictions", 8);
114            digester.addCallParam(
115                    "configuration/pool/tests-per-eviction", 9);
116            digester.addCallParam(
117                    "configuration/pool/idle-timeout", 10);
118            digester.addCallParam(
119                    "configuration/pool/test-while-idle", 11);
120            digester.addCallParam(
121                    "configuration/pool/lifo", 12);
122            digester.addCallParam(
123                    "configuration/pool/type", 13);
124            digester.addCallParam(
125                    "configuration/pool/sampling-rate", 14);
126            digester.addCallMethod("configuration/abandoned-config",
127                    "configureAbandonedConfig", 3);
128            digester.addCallParam(
129                    "configuration/abandoned-config/log-abandoned", 0);
130            digester.addCallParam(
131                    "configuration/abandoned-config/remove-abandoned", 1);
132            digester.addCallParam(
133                    "configuration/abandoned-config/abandoned-timeout", 2); 
134            
135            this.configFile = "config-pool.xml";
136            
137        }
138        
139        /**
140         * Create object pool and factory
141         */
142        protected void init() throws Exception {
143            // Create factory
144            WaiterFactory factory = new WaiterFactory(activateLatency, destroyLatency,
145                    makeLatency, passivateLatency, validateLatency, waiterLatency,
146                    maxActive, maxActivePerKey); 
147            
148            // Create object pool
149            if (poolType.equals("GenericObjectPool")) {
150                genericObjectPool = new GenericObjectPool(factory);
151                genericObjectPool.setMaxActive(maxActive);
152                genericObjectPool.setWhenExhaustedAction(exhaustedAction);
153                genericObjectPool.setMaxWait(maxWait);
154                genericObjectPool.setMaxIdle(maxIdle);
155                genericObjectPool.setMinIdle(minIdle);
156                genericObjectPool.setTestOnBorrow(testOnBorrow);
157                genericObjectPool.setTestOnReturn(testOnReturn);
158                genericObjectPool.setTimeBetweenEvictionRunsMillis(
159                        timeBetweenEvictions);
160                genericObjectPool.setNumTestsPerEvictionRun(testsPerEviction);
161                genericObjectPool.setMinEvictableIdleTimeMillis(idleTimeout);
162                genericObjectPool.setTestWhileIdle(testWhileIdle);
163                //genericObjectPool.setLifo(lifo);
164            } else if (poolType.equals("AbandonedObjectPool")) {
165                genericObjectPool = new AbandonedObjectPool(null,abandonedConfig);
166                genericObjectPool.setMaxActive(maxActive);
167                genericObjectPool.setWhenExhaustedAction(exhaustedAction);
168                genericObjectPool.setMaxWait(maxWait);
169                genericObjectPool.setMaxIdle(maxIdle);
170                genericObjectPool.setMinIdle(minIdle);
171                genericObjectPool.setTestOnBorrow(testOnBorrow);
172                genericObjectPool.setTestOnReturn(testOnReturn);
173                genericObjectPool.setTimeBetweenEvictionRunsMillis(
174                        timeBetweenEvictions);
175                genericObjectPool.setNumTestsPerEvictionRun(testsPerEviction);
176                genericObjectPool.setMinEvictableIdleTimeMillis(idleTimeout);
177                genericObjectPool.setTestWhileIdle(testWhileIdle);
178                //genericObjectPool.setLifo(lifo);
179                genericObjectPool.setFactory(factory);
180            } else if (poolType.equals("GenericKeyedObjectPool")) {
181                genericKeyedObjectPool = new GenericKeyedObjectPool();
182                genericKeyedObjectPool.setMaxActive(maxActivePerKey);
183                genericKeyedObjectPool.setMaxTotal(maxActive);
184                genericKeyedObjectPool.setWhenExhaustedAction(exhaustedAction);
185                genericKeyedObjectPool.setMaxWait(maxWait);
186                genericKeyedObjectPool.setMaxIdle(maxIdle);
187                genericKeyedObjectPool.setMinIdle(minIdle);
188                genericKeyedObjectPool.setTestOnBorrow(testOnBorrow);
189                genericKeyedObjectPool.setTestOnReturn(testOnReturn);
190                genericKeyedObjectPool.setTimeBetweenEvictionRunsMillis(
191                        timeBetweenEvictions);
192                genericKeyedObjectPool.setNumTestsPerEvictionRun(testsPerEviction);
193                genericKeyedObjectPool.setMinEvictableIdleTimeMillis(idleTimeout);
194                genericKeyedObjectPool.setTestWhileIdle(testWhileIdle);
195                //genericKeyedObjectPool.setLifo(lifo);
196                genericKeyedObjectPool.setFactory(factory);
197            } else if (poolType.equals("StackObjectPool")) {
198                stackObjectPool = new StackObjectPool();
199                stackObjectPool.setFactory(factory);
200            } else if (poolType.equals("SoftReferenceObjectPool")) {
201                softReferenceObjectPool = new SoftReferenceObjectPool();
202                softReferenceObjectPool.setFactory(factory);
203            } else if (poolType.equals("StackKeyedObjectPool")) {
204                stackKeyedObjectPool = new StackKeyedObjectPool();
205                stackKeyedObjectPool.setFactory(factory);
206            } else {
207                throw new ConfigurationException(
208                        "invalid pool type configuration: " + poolType);
209            }
210            
211            logger.info("Initialized pool with properties: ");
212            logger.info(" poolTypeT: " + poolType);
213            logger.info(" exhaustedAction: " + exhaustedAction);
214            logger.info(" maxActive: " + maxActive);
215            logger.info(" maxActivePerKey: " + maxActivePerKey);
216            logger.info(" maxIdle: " + maxIdle);
217            logger.info(" minIdle: " + minIdle);
218            logger.info(" testOnBorrow: " + testOnBorrow);
219            logger.info(" testWhileIdle: " + testWhileIdle);
220            logger.info(" timeBetweenEvictions: " + timeBetweenEvictions);
221            logger.info(" testsPerEviction: " + testsPerEviction);
222            logger.info(" idleTimeout: " + idleTimeout);
223            logger.info(" lifo: " + lifo);
224            logger.info(" abandonedConfig: ");
225            logger.info("  logAbandoned: " +
226                    abandonedConfig.getLogAbandoned());
227            logger.info("  removeAbandoned: " +
228                    abandonedConfig.getRemoveAbandoned());
229            logger.info("  abandonedTimeout: " + 
230                    abandonedConfig.getRemoveAbandonedTimeout()); 
231        }
232        
233        /**
234         * Close object pool
235         */
236        protected void cleanUp() throws Exception {
237            if (genericObjectPool != null) {
238                genericObjectPool.close();
239            }
240            if (genericKeyedObjectPool != null) {
241                genericKeyedObjectPool.close();
242            }
243            if (stackObjectPool != null) {
244                stackObjectPool.close();
245            }
246            if (softReferenceObjectPool != null) {
247                softReferenceObjectPool.close();
248            }
249            if (stackKeyedObjectPool != null) {
250                stackKeyedObjectPool.close();
251            }
252        }
253        
254        /**
255         * Create and return a PoolClientThread
256         */
257        protected ClientThread makeClientThread(long iterations, long minDelay,
258                long maxDelay, double sigma, String delayType, long rampPeriod,
259                long peakPeriod, long troughPeriod, String cycleType, 
260                String rampType, Logger logger, Statistics stats) {
261            if (poolType.equals("GenericObjectPool")) {
262                return new PoolClientThread(iterations, minDelay, maxDelay,
263                        sigma, delayType, rampPeriod, peakPeriod, troughPeriod,
264                        cycleType, rampType, logger, stats, genericObjectPool, 
265                        samplingRate);
266            }
267            if (poolType.equals("GenericKeyedObjectPool")) {
268                return new PoolClientThread(iterations, minDelay, maxDelay,
269                        sigma, delayType, rampPeriod, peakPeriod, troughPeriod,
270                        cycleType, rampType, logger, stats,
271                        genericKeyedObjectPool, samplingRate);
272            }
273            if (poolType.equals("StackKeyedObjectPool")) {
274                return new PoolClientThread(iterations, minDelay, maxDelay,
275                        sigma, delayType, rampPeriod, peakPeriod, troughPeriod,
276                        cycleType, rampType, logger, stats,
277                        stackKeyedObjectPool, samplingRate);
278            }
279            if (poolType.equals("StackObjectPool")) {
280                return new PoolClientThread(iterations, minDelay, maxDelay,
281                        sigma, delayType, rampPeriod, peakPeriod, troughPeriod,
282                        cycleType, rampType, logger, stats,
283                        stackObjectPool, samplingRate);
284            }
285            if (poolType.equals("SoftReferenceObjectPool")) {
286                return new PoolClientThread(iterations, minDelay, maxDelay,
287                        sigma, delayType, rampPeriod, peakPeriod, troughPeriod,
288                        cycleType, rampType, logger, stats,
289                        softReferenceObjectPool, samplingRate);
290            }
291            return null;
292        }
293        
294        // ------------------------------------------------------------------------
295        // Configuration methods specific to this LoadGenerator invoked by Digester
296        // when superclass execute calls digerster.parse.
297        // ------------------------------------------------------------------------       
298        public void configureFactory(String activateLatency, String destroyLatency,
299                String makeLatency, String passivateLatency, String validateLatency,
300                String waiterLatency) {
301           
302            this.activateLatency = Long.parseLong(activateLatency);
303            this.destroyLatency = Long.parseLong(destroyLatency);
304            this.makeLatency = Long.parseLong(makeLatency);
305            this.passivateLatency = Long.parseLong(passivateLatency);
306            this.validateLatency = Long.parseLong(validateLatency);
307            this.waiterLatency = Long.parseLong(waiterLatency);
308        }
309        
310        public void configurePool(String maxActive, String maxActivePerKey,
311                String maxIdle, String minIdle, String maxWait,
312                String exhaustedAction, String testOnBorrow,
313                String testOnReturn, String timeBetweenEvictions,
314                String testsPerEviction, String idleTimeout, 
315                String testWhileIdle, String lifo, String type, String samplingRate)
316            throws ConfigurationException { 
317            this.maxActive = Integer.parseInt(maxActive);
318            this.maxActivePerKey = Integer.parseInt(maxActivePerKey);
319            this.maxIdle = Integer.parseInt(maxIdle);
320            this.minIdle = Integer.parseInt(minIdle);
321            this.maxWait = Long.parseLong(maxWait);
322            this.testOnBorrow = Boolean.parseBoolean(testOnBorrow);
323            this.testOnReturn = Boolean.parseBoolean(testOnReturn);
324            this.timeBetweenEvictions = Long.parseLong(timeBetweenEvictions);
325            this.testsPerEviction = Integer.parseInt(testsPerEviction);
326            this.idleTimeout = Long.parseLong(idleTimeout);
327            this.testWhileIdle = Boolean.parseBoolean(testWhileIdle);
328            this.lifo = Boolean.parseBoolean(lifo);
329            this.poolType = type;
330            if (exhaustedAction.equals("block")) {
331                this.exhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_BLOCK;
332            } else if (exhaustedAction.equals("fail")) {
333                this.exhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_FAIL;
334            } else if (exhaustedAction.equals("grow")) {
335                this.exhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
336            } else { 
337                throw new ConfigurationException(
338                "Bad configuration setting for exhausted action: "
339                        + exhaustedAction); 
340            }  
341            this.samplingRate = Double.parseDouble(samplingRate);
342        }
343        
344        public void configureAbandonedConfig(String logAbandoned,
345                String removeAbandoned, String abandonedTimeout) {
346            abandonedConfig.setLogAbandoned(Boolean.parseBoolean(logAbandoned));
347            abandonedConfig.setRemoveAbandoned(
348                    Boolean.parseBoolean(removeAbandoned));
349            abandonedConfig.setRemoveAbandonedTimeout(
350                    Integer.parseInt(abandonedTimeout));
351        }
352        
353        // Pool getters for unit tests
354        protected GenericObjectPool getGenericObjectPool() {
355            return genericObjectPool;
356        }
357        
358        protected GenericKeyedObjectPool getGenericKeyedObjectPool() {
359            return genericKeyedObjectPool;
360        }
361        
362    }