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 }