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 }