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 }