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  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 }