View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.pool;
19  
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.Iterator;
23  
24  import org.apache.commons.pool.PoolableObjectFactory;
25  import org.apache.commons.pool.KeyedPoolableObjectFactory;
26  
27  /**
28   * Object factory with configurable latencies for object lifecycle methods.
29   * This factory will also track and enforce maxActive, maxActivePerKey contracts.
30   * If the factory's maxActive / maxActivePerKey are set to match those of the
31   * pool, makeObject will throw IllegalStateException if the number of makes - destroys
32   * (per key) exceeds the configured max.
33   *
34   */
35  public class WaiterFactory implements PoolableObjectFactory,
36  KeyedPoolableObjectFactory {
37      
38      /** Latency of activateObject */
39      private final long activateLatency;
40      
41      /** Latency of destroyObject */
42      private final long destroyLatency;
43      
44      /** Latency of makeObject */
45      private final long makeLatency;
46      
47      /** Latency of passivateObject */
48      private final long passivateLatency;
49      
50      /** Latency of validateObject */
51      private final long validateLatency;
52      
53      /** Latency of doWait for Waiter instances created by this factory */
54      private final long waiterLatency;
55      
56      /** Probability that passivation will invalidate Waiter instances */
57      private final double passivateInvalidationProbability;
58      
59      /** Count of (makes - destroys) since last reset */
60      private long activeCount = 0;
61      
62      /** Count of (makes - destroys) per key since last reset */
63      private Map activeCounts = new HashMap();
64      
65      /** Maximum of (makes - destroys) - if exceeded IllegalStateException */
66      private final long maxActive;  // GKOP 1.x calls this maxTotal
67      
68      /** Maximum of (makes - destroys) per key */
69      private final long maxActivePerKey;  // GKOP 1.x calls this maxActive
70  
71      public WaiterFactory(long activateLatency, long destroyLatency,
72              long makeLatency, long passivateLatency, long validateLatency,
73              long waiterLatency,long maxActive, long maxActivePerKey,
74              double passivateInvalidationProbability) {
75          this.activateLatency = activateLatency;
76          this.destroyLatency = destroyLatency;
77          this.makeLatency = makeLatency;
78          this.passivateLatency = passivateLatency;
79          this.validateLatency = validateLatency;
80          this.waiterLatency = waiterLatency;
81          this.maxActive = maxActive;
82          this.maxActivePerKey = maxActivePerKey;
83          this.passivateInvalidationProbability = passivateInvalidationProbability;
84      }
85      
86      public WaiterFactory(long activateLatency, long destroyLatency,
87              long makeLatency, long passivateLatency, long validateLatency,
88              long waiterLatency) {
89          this(activateLatency, destroyLatency, makeLatency, passivateLatency,
90                  validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
91      }
92      
93      public WaiterFactory(long activateLatency, long destroyLatency,
94              long makeLatency, long passivateLatency, long validateLatency,
95              long waiterLatency,long maxActive) {
96          this(activateLatency, destroyLatency, makeLatency, passivateLatency,
97                  validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
98      }
99  
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 }