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 * https://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.time.Duration;
21 import java.util.concurrent.atomic.AtomicInteger;
22
23 import org.apache.commons.lang3.ThreadUtils;
24
25 /**
26 * <p>Object created by {@link WaiterFactory}. Maintains active / valid state,
27 * last passivated and idle times. Waits with configurable latency when
28 * {@link #doWait()} method is called.</p>
29 *
30 * <p>This class is *not* threadsafe.</p>
31 */
32 public class Waiter {
33 private static final AtomicInteger instanceCount = new AtomicInteger();
34
35 /**
36 * Sleeps for the given duration while ignoring {@link InterruptedException}.
37 * <p>
38 * The sleep duration may be shorter than duration if we catch a {@link InterruptedException}.
39 * </p>
40 *
41 * @param millis the length of time to sleep in milliseconds.
42 */
43 public static void sleepQuietly(final long millis) {
44 ThreadUtils.sleepQuietly(Duration.ofMillis(millis));
45 }
46
47 private boolean active;
48 private boolean valid;
49 private long latency;
50 private long lastPassivatedMillis;
51 private long lastIdleTimeMillis;
52 private long passivationCount;
53 private long validationCount;
54
55 private final int id = instanceCount.getAndIncrement();
56
57 public Waiter(final boolean active, final boolean valid, final long latency) {
58 this.active = active;
59 this.valid = valid;
60 this.latency = latency;
61 this.lastPassivatedMillis = System.currentTimeMillis();
62 }
63
64 /**
65 * Wait for {@link #getLatency()} milliseconds.
66 */
67 public void doWait() {
68 sleepQuietly(latency);
69 }
70
71 @Override
72 public boolean equals(final Object obj) {
73 if (!(obj instanceof Waiter)) {
74 return false;
75 }
76 return obj.hashCode() == id;
77 }
78
79 /**
80 * <p>Returns the last idle time for this instance in ms.</p>
81 *
82 * <p>When an instance is created, and each subsequent time it is passivated,
83 * the {@link #getLastPassivatedMillis() lastPassivated} property is updated with the
84 * current time. When the next activation occurs, {@code lastIdleTime} is
85 * updated with the elapsed time since passivation.<p>
86 *
87 * @return last idle time
88 */
89 public long getLastIdleTimeMillis() {
90 return lastIdleTimeMillis;
91 }
92
93 /**
94 * <p>Returns the system time of this instance's last passivation.</p>
95 *
96 * <p>When an instance is created, this field is initialized to the system time.</p>
97 *
98 * @return time of last passivation
99 */
100 public long getLastPassivatedMillis() {
101 return lastPassivatedMillis;
102 }
103
104 public long getLatency() {
105 return latency;
106 }
107
108 /**
109 * @return how many times this instance has been passivated
110 */
111 public long getPassivationCount() {
112 return passivationCount;
113 }
114
115 /**
116 * @return how many times this instance has been validated
117 */
118 public long getValidationCount() {
119 return validationCount;
120 }
121
122 @Override
123 public int hashCode() {
124 return id;
125 }
126
127 /**
128 * Tests whether or not the instance is active.
129 *
130 * @return true if the last lifecycle event for this instance was activation.
131 */
132 public boolean isActive() {
133 return active;
134 }
135
136 public boolean isValid() {
137 validationCount++;
138 return valid;
139 }
140
141 /**
142 * <p>Sets the active state and updates {@link #getLastIdleTimeMillis() lastIdleTime}
143 * or {@link #getLastPassivatedMillis() lastPassivated} as appropriate.</p>
144 *
145 * <p>If the active state is changing from inactive to active, lastIdleTime
146 * is updated with the current time minus lastPassivated. If the state is
147 * changing from active to inactive, lastPassivated is updated with the
148 * current time.</p>
149 *
150 * <p>{@link WaiterFactory#activateObject(PooledObject)} and
151 * {@link WaiterFactory#passivateObject(PooledObject)} invoke this method on
152 * their actual parameter, passing {@code true} and {@code false},
153 * respectively.</p>
154 *
155 * @param active new active state
156 */
157 public void setActive(final boolean active) {
158 final boolean activeState = this.active;
159 if (activeState == active) {
160 return;
161 }
162 this.active = active;
163 final long currentTimeMillis = System.currentTimeMillis();
164 if (active) { // activating
165 lastIdleTimeMillis = currentTimeMillis - lastPassivatedMillis;
166 } else { // passivating
167 lastPassivatedMillis = currentTimeMillis;
168 passivationCount++;
169 }
170 }
171
172 public void setLatency(final long latency) {
173 this.latency = latency;
174 }
175
176 public void setValid(final boolean valid) {
177 this.valid = valid;
178 }
179
180 @Override
181 public String toString() {
182 final StringBuilder buff = new StringBuilder();
183 buff.append("ID = " + id + '\n');
184 buff.append("valid = " + valid + '\n');
185 buff.append("active = " + active + '\n');
186 buff.append("lastPassivated = " + lastPassivatedMillis + '\n');
187 buff.append("lastIdleTimeMs = " + lastIdleTimeMillis + '\n');
188 buff.append("latency = " + latency + '\n');
189 return buff.toString();
190 }
191 }