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 package org.apache.commons.pool2.impl;
18
19 import java.lang.reflect.ParameterizedType;
20 import java.lang.reflect.Type;
21 import java.lang.reflect.TypeVariable;
22 import java.time.Duration;
23 import java.time.Instant;
24 import java.time.temporal.ChronoUnit;
25 import java.util.Objects;
26 import java.util.concurrent.TimeUnit;
27
28 import org.apache.commons.pool2.PooledObjectFactory;
29
30 /**
31 * Implementation specific utilities.
32 *
33 * @since 2.0
34 */
35 final class PoolImplUtils {
36
37 /**
38 * Identifies the concrete type of object that an object factory creates.
39 *
40 * @param factoryClass The factory to examine
41 * @return the type of object the factory creates
42 */
43 @SuppressWarnings("rawtypes")
44 static Class<?> getFactoryType(final Class<? extends PooledObjectFactory> factoryClass) {
45 final Class<PooledObjectFactory> type = PooledObjectFactory.class;
46 final Object genericType = getGenericType(type, factoryClass);
47 if (genericType instanceof Integer) {
48 // POOL-324 org.apache.commons.pool2.impl.GenericObjectPool.getFactoryType() throws
49 // java.lang.ClassCastException
50 //
51 // A bit hackish, but we must handle cases when getGenericType() does not return a concrete types.
52 final ParameterizedType pi = getParameterizedType(type, factoryClass);
53 if (pi != null) {
54 final Type[] bounds = ((TypeVariable) pi.getActualTypeArguments()[((Integer) genericType).intValue()])
55 .getBounds();
56 if (bounds != null && bounds.length > 0) {
57 final Type bound0 = bounds[0];
58 if (bound0 instanceof Class) {
59 return (Class<?>) bound0;
60 }
61 }
62 }
63 // last resort: Always return a Class
64 return Object.class;
65 }
66 return (Class<?>) genericType;
67 }
68
69 /**
70 * Gets the concrete type used by an implementation of an interface that uses a generic type.
71 *
72 * @param type The interface that defines a generic type
73 * @param clazz The class that implements the interface with a concrete type
74 * @param <T> The interface type
75 * @return concrete type used by the implementation
76 */
77 private static <T> Object getGenericType(final Class<T> type, final Class<? extends T> clazz) {
78 if (type == null || clazz == null) {
79 // Error will be logged further up the call stack
80 return null;
81 }
82
83 // Look to see if this class implements the generic interface
84 final ParameterizedType pi = getParameterizedType(type, clazz);
85 if (pi != null) {
86 return getTypeParameter(clazz, pi.getActualTypeArguments()[0]);
87 }
88
89 // Interface not found on this class. Look at the superclass.
90 @SuppressWarnings("unchecked")
91 final Class<? extends T> superClass = (Class<? extends T>) clazz.getSuperclass();
92
93 final Object result = getGenericType(type, superClass);
94 if (result instanceof Class<?>) {
95 // Superclass implements interface and defines explicit type for generic
96 return result;
97 }
98 if (result instanceof Integer) {
99 // Superclass implements interface and defines unknown type for generic
100 // Map that unknown type to the generic types defined in this class
101 final ParameterizedType superClassType = (ParameterizedType) clazz.getGenericSuperclass();
102 return getTypeParameter(clazz, superClassType.getActualTypeArguments()[((Integer) result).intValue()]);
103 }
104 // Error will be logged further up the call stack
105 return null;
106 }
107
108 /**
109 * Gets the matching parameterized type or null.
110 *
111 * @param type The interface that defines a generic type.
112 * @param clazz The class that implements the interface with a concrete type.
113 * @param <T> The interface type.
114 * @return the matching parameterized type or null.
115 */
116 private static <T> ParameterizedType getParameterizedType(final Class<T> type, final Class<? extends T> clazz) {
117 for (final Type iface : clazz.getGenericInterfaces()) {
118 // Only need to check interfaces that use generics
119 if (iface instanceof ParameterizedType) {
120 final ParameterizedType pi = (ParameterizedType) iface;
121 // Look for the generic interface
122 if (pi.getRawType() instanceof Class && type.isAssignableFrom((Class<?>) pi.getRawType())) {
123 return pi;
124 }
125 }
126 }
127 return null;
128 }
129
130 /**
131 * For a generic parameter, return either the Class used or if the type is unknown, the index for the type in
132 * definition of the class
133 *
134 * @param clazz defining class
135 * @param argType the type argument of interest
136 * @return An instance of {@link Class} representing the type used by the type parameter or an instance of
137 * {@link Integer} representing the index for the type in the definition of the defining class
138 */
139 private static Object getTypeParameter(final Class<?> clazz, final Type argType) {
140 if (argType instanceof Class<?>) {
141 return argType;
142 }
143 final TypeVariable<?>[] tvs = clazz.getTypeParameters();
144 for (int i = 0; i < tvs.length; i++) {
145 if (tvs[i].equals(argType)) {
146 return Integer.valueOf(i);
147 }
148 }
149 return null;
150 }
151
152 static boolean isPositive(final Duration delay) {
153 return delay != null && !delay.isNegative() && !delay.isZero();
154 }
155
156 /**
157 * Returns the greater of two {@code Instant} values. That is, the result is the argument closer to the value of
158 * {@link Instant#MAX}. If the arguments have the same value, the result is that same value.
159 *
160 * @param a an argument.
161 * @param b another argument.
162 * @return the larger of {@code a} and {@code b}.
163 */
164 static Instant max(final Instant a, final Instant b) {
165 return a.compareTo(b) > 0 ? a : b;
166 }
167
168 /**
169 * Returns the smaller of two {@code Instant} values. That is, the result is the argument closer to the value of
170 * {@link Instant#MIN}. If the arguments have the same value, the result is that same value.
171 *
172 * @param a an argument.
173 * @param b another argument.
174 * @return the smaller of {@code a} and {@code b}.
175 */
176 static Instant min(final Instant a, final Instant b) {
177 return a.compareTo(b) < 0 ? a : b;
178 }
179
180 /**
181 * Returns a non-null duration, value if non-null, otherwise defaultValue.
182 *
183 * @param value May be null.
184 * @param defaultValue May not be null/
185 * @return value if non-null, otherwise defaultValue.
186 */
187 static Duration nonNull(final Duration value, final Duration defaultValue) {
188 return value != null ? value : Objects.requireNonNull(defaultValue, "defaultValue");
189 }
190
191 /**
192 * Converts a {@link TimeUnit} to a {@link ChronoUnit}.
193 *
194 * @param timeUnit A TimeUnit.
195 * @return The corresponding ChronoUnit.
196 */
197 static ChronoUnit toChronoUnit(final TimeUnit timeUnit) {
198 // TODO when using Java >= 9: Use TimeUnit.toChronoUnit().
199 switch (Objects.requireNonNull(timeUnit)) {
200 case NANOSECONDS:
201 return ChronoUnit.NANOS;
202 case MICROSECONDS:
203 return ChronoUnit.MICROS;
204 case MILLISECONDS:
205 return ChronoUnit.MILLIS;
206 case SECONDS:
207 return ChronoUnit.SECONDS;
208 case MINUTES:
209 return ChronoUnit.MINUTES;
210 case HOURS:
211 return ChronoUnit.HOURS;
212 case DAYS:
213 return ChronoUnit.DAYS;
214 default:
215 throw new IllegalArgumentException(timeUnit.toString());
216 }
217 }
218
219 /**
220 * Converts am amount and TimeUnit into a Duration.
221 *
222 * @param amount the amount of the duration, measured in terms of the unit, positive or negative
223 * @param timeUnit the unit that the duration is measured in, must have an exact duration, not null
224 * @return a Duration.
225 */
226 static Duration toDuration(final long amount, final TimeUnit timeUnit) {
227 return Duration.of(amount, toChronoUnit(timeUnit));
228 }
229
230 }