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.performance.pool;
19  
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.Iterator;
23  import java.util.logging.Logger;
24  import java.util.logging.Level;
25  import java.util.concurrent.atomic.AtomicInteger;
26  
27  import org.apache.commons.pool.PoolableObjectFactory;
28  import org.apache.commons.pool.KeyedPoolableObjectFactory;
29  
30  /**
31   * Object factory with configurable latencies for object lifecycle methods.
32   *
33   */
34  public class WaiterFactory implements PoolableObjectFactory,
35  KeyedPoolableObjectFactory {
36      
37      // TODO: implement protected getters so these can be stochastic
38      private long activateLatency = 0;
39      private long destroyLatency = 0;
40      private long makeLatency = 0;
41      private long passivateLatency = 0;
42      private long validateLatency = 0;
43      private long waiterLatency = 0;
44      
45      /** Count of (makes - destroys) since last reset */
46      private long activeCount = 0;
47      
48      /** Count of (makes - destroys) per key since last reset */
49      private Map<Object, AtomicInteger> activeCounts = 
50          new HashMap<Object, AtomicInteger>();
51      
52      /** Maximum of (makes - destroys) - if exceeded IllegalStateException */
53      private long maxActive = Long.MAX_VALUE;
54      
55      /** Maximum of (makes - destroys) per key */
56      private long maxActivePerKey = Long.MAX_VALUE;
57      
58      protected static final Logger logger = 
59          Logger.getLogger(WaiterFactory.class.getName());
60  
61      public WaiterFactory(long activateLatency, long destroyLatency,
62              long makeLatency, long passivateLatency, long validateLatency,
63              long waiterLatency,long maxActive, long maxActivePerKey) {
64          this.activateLatency = activateLatency;
65          this.destroyLatency = destroyLatency;
66          this.makeLatency = makeLatency;
67          this.passivateLatency = passivateLatency;
68          this.validateLatency = validateLatency;
69          this.waiterLatency = waiterLatency;
70          this.maxActive = maxActive;
71          this.maxActivePerKey = maxActivePerKey;
72      }
73      
74      public WaiterFactory(long activateLatency, long destroyLatency,
75              long makeLatency, long passivateLatency, long validateLatency,
76              long waiterLatency) {
77          this(activateLatency, destroyLatency, makeLatency, passivateLatency,
78                  validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE);
79      }
80      
81      public WaiterFactory(long activateLatency, long destroyLatency,
82              long makeLatency, long passivateLatency, long validateLatency,
83              long waiterLatency,long maxActive) {
84          this(activateLatency, destroyLatency, makeLatency, passivateLatency,
85                  validateLatency, waiterLatency, maxActive, Long.MAX_VALUE);
86      }
87  
88      public void activateObject(Object arg0) throws Exception {
89          if (logger.isLoggable(Level.FINEST)) {
90              logger.finest("activate");
91          }
92          doWait(activateLatency);
93      }
94  
95      public void destroyObject(Object arg0) throws Exception {
96          if (logger.isLoggable(Level.FINE)) {
97              logger.fine("destroy");
98          }
99          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 }