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.pool2;
19  
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import org.apache.commons.pool2.impl.DefaultPooledObject;
24  
25  /**
26   * Object factory with configurable latencies for object lifecycle methods.
27   * This factory will also track and enforce maxActive, maxActivePerKey contracts.
28   * If the factory's maxActive / maxActivePerKey are set to match those of the
29   * pool, makeObject will throw IllegalStateException if the number of makes - destroys
30   * (per key) exceeds the configured max.
31   *
32   * @param <K> The type of keys managed by this factory.
33   */
34  public class WaiterFactory<K> implements PooledObjectFactory<Waiter>, KeyedPooledObjectFactory<K, Waiter> {
35  
36      /** Integer value 0. */
37      private static final Integer ZERO = Integer.valueOf(0);
38  
39      /** Integer value 1. */
40      private static final Integer ONE = Integer.valueOf(1);
41  
42      /** Latency of activateObject */
43      private final long activateLatency;
44  
45      /** Latency of destroyObject */
46      private final long destroyLatency;
47  
48      /** Latency of makeObject */
49      private final long makeLatency;
50  
51      /** Latency of passivateObject */
52      private final long passivateLatency;
53  
54      /** Latency of validateObject */
55      private final long validateLatency;
56  
57      /** Latency of doWait for Waiter instances created by this factory */
58      private final long waiterLatency;
59  
60      /** Probability that passivation will invalidate Waiter instances */
61      private final double passivateInvalidationProbability;
62  
63      /** Count of (makes - destroys) since last reset */
64      private long activeCount;
65  
66      /** Count of (makes - destroys) per key since last reset */
67      private final Map<K, Integer> activeCounts = new HashMap<>();
68  
69      /** Maximum of (makes - destroys) - if exceeded IllegalStateException */
70      private final long maxActive;  // GKOP 1.x calls this maxTotal
71  
72      /** Maximum of (makes - destroys) per key */
73      private final long maxActivePerKey;  // GKOP 1.x calls this maxActive
74  
75      public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency,
76              final long waiterLatency) {
77          this(activateLatency, destroyLatency, makeLatency, passivateLatency, validateLatency, waiterLatency, Long.MAX_VALUE, Long.MAX_VALUE, 0);
78      }
79  
80      public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency,
81              final long waiterLatency, final long maxActive) {
82          this(activateLatency, destroyLatency, makeLatency, passivateLatency, validateLatency, waiterLatency, maxActive, Long.MAX_VALUE, 0);
83      }
84  
85      public WaiterFactory(final long activateLatency, final long destroyLatency, final long makeLatency, final long passivateLatency, final long validateLatency,
86              final long waiterLatency, final long maxActive, final long maxActivePerKey, final double passivateInvalidationProbability) {
87          this.activateLatency = activateLatency;
88          this.destroyLatency = destroyLatency;
89          this.makeLatency = makeLatency;
90          this.passivateLatency = passivateLatency;
91          this.validateLatency = validateLatency;
92          this.waiterLatency = waiterLatency;
93          this.maxActive = maxActive;
94          this.maxActivePerKey = maxActivePerKey;
95          this.passivateInvalidationProbability = passivateInvalidationProbability;
96      }
97  
98      @Override
99      public void activateObject(final K key, final PooledObject<Waiter> obj) {
100         activateObject(obj);
101     }
102 
103     @Override
104     public void activateObject(final PooledObject<Waiter> obj) {
105         doWait(activateLatency);
106         obj.getObject().setActive(true);
107     }
108 
109     @Override
110     public void destroyObject(final K key, final PooledObject<Waiter> obj) {
111         destroyObject(obj);
112         synchronized (this) {
113             activeCounts.computeIfPresent(key, (k, v) -> Integer.valueOf(v.intValue() - 1));
114         }
115     }
116 
117     @Override
118     public void destroyObject(final PooledObject<Waiter> obj) {
119         doWait(destroyLatency);
120         obj.getObject().setValid(false);
121         obj.getObject().setActive(false);
122         // Decrement *after* destroy
123         synchronized (this) {
124             activeCount--;
125         }
126     }
127 
128     protected void doWait(final long latency) {
129         if (latency == 0) {
130             return;
131         }
132         Waiter.sleepQuietly(latency);
133     }
134 
135     /**
136      * @return the maxActive
137      */
138     public synchronized long getMaxActive() {
139         return maxActive;
140     }
141 
142     @Override
143     public PooledObject<Waiter> makeObject() {
144         // Increment and test *before* make
145         synchronized (this) {
146             if (activeCount >= maxActive) {
147                 throw new IllegalStateException("Too many active instances: " +
148                 activeCount + " in circulation with maxActive = " + maxActive);
149             }
150             activeCount++;
151         }
152         doWait(makeLatency);
153         return new DefaultPooledObject<>(new Waiter(false, true, waiterLatency));
154     }
155 
156     @Override
157     public PooledObject<Waiter> makeObject(final K key) {
158         synchronized (this) {
159             activeCounts.merge(key, ONE, (v1, v2) -> {
160                 if (v1.intValue() >= maxActivePerKey) {
161                     throw new IllegalStateException("Too many active " +
162                     "instances for key = " + key + ": " + v1.intValue() +
163                     " in circulation " + "with maxActivePerKey = " +
164                     maxActivePerKey);
165                 }
166                 return Integer.valueOf(v1.intValue() + 1);
167             });
168         }
169         return makeObject();
170     }
171 
172     @Override
173     public void passivateObject(final K key, final PooledObject<Waiter> obj) {
174         passivateObject(obj);
175     }
176 
177     @Override
178     public void passivateObject(final PooledObject<Waiter> obj) {
179         obj.getObject().setActive(false);
180         doWait(passivateLatency);
181         if (Math.random() < passivateInvalidationProbability) {
182             obj.getObject().setValid(false);
183         }
184     }
185 
186     public synchronized void reset() {
187         activeCount = 0;
188         if (activeCounts.isEmpty()) {
189             return;
190         }
191         for (K key : activeCounts.keySet()) {
192             activeCounts.put(key, ZERO);
193         }
194     }
195 
196     @Override
197     public boolean validateObject(final K key, final PooledObject<Waiter> obj) {
198         return validateObject(obj);
199     }
200 
201     @Override
202     public boolean validateObject(final PooledObject<Waiter> obj) {
203         doWait(validateLatency);
204         return obj.getObject().isValid();
205     }
206 
207 }