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