PoolImplUtils.java

  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. import java.lang.reflect.ParameterizedType;
  19. import java.lang.reflect.Type;
  20. import java.lang.reflect.TypeVariable;
  21. import java.time.Duration;
  22. import java.time.Instant;
  23. import java.time.temporal.ChronoUnit;
  24. import java.util.Objects;
  25. import java.util.concurrent.TimeUnit;

  26. import org.apache.commons.pool2.PooledObjectFactory;

  27. /**
  28.  * Implementation specific utilities.
  29.  *
  30.  * @since 2.0
  31.  */
  32. final class PoolImplUtils {

  33.     /**
  34.      * Identifies the concrete type of object that an object factory creates.
  35.      *
  36.      * @param factoryClass The factory to examine
  37.      * @return the type of object the factory creates
  38.      */
  39.     @SuppressWarnings("rawtypes")
  40.     static Class<?> getFactoryType(final Class<? extends PooledObjectFactory> factoryClass) {
  41.         final Class<PooledObjectFactory> type = PooledObjectFactory.class;
  42.         final Object genericType = getGenericType(type, factoryClass);
  43.         if (genericType instanceof Integer) {
  44.             // POOL-324 org.apache.commons.pool2.impl.GenericObjectPool.getFactoryType() throws
  45.             // java.lang.ClassCastException
  46.             //
  47.             // A bit hackish, but we must handle cases when getGenericType() does not return a concrete types.
  48.             final ParameterizedType pi = getParameterizedType(type, factoryClass);
  49.             if (pi != null) {
  50.                 final Type[] bounds = ((TypeVariable) pi.getActualTypeArguments()[((Integer) genericType).intValue()])
  51.                         .getBounds();
  52.                 if (bounds != null && bounds.length > 0) {
  53.                     final Type bound0 = bounds[0];
  54.                     if (bound0 instanceof Class) {
  55.                         return (Class<?>) bound0;
  56.                     }
  57.                 }
  58.             }
  59.             // last resort: Always return a Class
  60.             return Object.class;
  61.         }
  62.         return (Class<?>) genericType;
  63.     }

  64.     /**
  65.      * Gets the concrete type used by an implementation of an interface that uses a generic type.
  66.      *
  67.      * @param type The interface that defines a generic type
  68.      * @param clazz The class that implements the interface with a concrete type
  69.      * @param <T> The interface type
  70.      * @return concrete type used by the implementation
  71.      */
  72.     private static <T> Object getGenericType(final Class<T> type, final Class<? extends T> clazz) {
  73.         if (type == null || clazz == null) {
  74.             // Error will be logged further up the call stack
  75.             return null;
  76.         }

  77.         // Look to see if this class implements the generic interface
  78.         final ParameterizedType pi = getParameterizedType(type, clazz);
  79.         if (pi != null) {
  80.             return getTypeParameter(clazz, pi.getActualTypeArguments()[0]);
  81.         }

  82.         // Interface not found on this class. Look at the superclass.
  83.         @SuppressWarnings("unchecked")
  84.         final Class<? extends T> superClass = (Class<? extends T>) clazz.getSuperclass();

  85.         final Object result = getGenericType(type, superClass);
  86.         if (result instanceof Class<?>) {
  87.             // Superclass implements interface and defines explicit type for generic
  88.             return result;
  89.         }
  90.         if (result instanceof Integer) {
  91.             // Superclass implements interface and defines unknown type for generic
  92.             // Map that unknown type to the generic types defined in this class
  93.             final ParameterizedType superClassType = (ParameterizedType) clazz.getGenericSuperclass();
  94.             return getTypeParameter(clazz, superClassType.getActualTypeArguments()[((Integer) result).intValue()]);
  95.         }
  96.         // Error will be logged further up the call stack
  97.         return null;
  98.     }

  99.     /**
  100.      * Gets the matching parameterized type or null.
  101.      *
  102.      * @param type The interface that defines a generic type.
  103.      * @param clazz The class that implements the interface with a concrete type.
  104.      * @param <T> The interface type.
  105.      * @return the matching parameterized type or null.
  106.      */
  107.     private static <T> ParameterizedType getParameterizedType(final Class<T> type, final Class<? extends T> clazz) {
  108.         for (final Type iface : clazz.getGenericInterfaces()) {
  109.             // Only need to check interfaces that use generics
  110.             if (iface instanceof ParameterizedType) {
  111.                 final ParameterizedType pi = (ParameterizedType) iface;
  112.                 // Look for the generic interface
  113.                 if (pi.getRawType() instanceof Class && type.isAssignableFrom((Class<?>) pi.getRawType())) {
  114.                     return pi;
  115.                 }
  116.             }
  117.         }
  118.         return null;
  119.     }

  120.     /**
  121.      * For a generic parameter, return either the Class used or if the type is unknown, the index for the type in
  122.      * definition of the class
  123.      *
  124.      * @param clazz defining class
  125.      * @param argType the type argument of interest
  126.      * @return An instance of {@link Class} representing the type used by the type parameter or an instance of
  127.      *         {@link Integer} representing the index for the type in the definition of the defining class
  128.      */
  129.     private static Object getTypeParameter(final Class<?> clazz, final Type argType) {
  130.         if (argType instanceof Class<?>) {
  131.             return argType;
  132.         }
  133.         final TypeVariable<?>[] tvs = clazz.getTypeParameters();
  134.         for (int i = 0; i < tvs.length; i++) {
  135.             if (tvs[i].equals(argType)) {
  136.                 return Integer.valueOf(i);
  137.             }
  138.         }
  139.         return null;
  140.     }

  141.     static boolean isPositive(final Duration delay) {
  142.         return delay != null && !delay.isNegative() && !delay.isZero();
  143.     }

  144.     /**
  145.      * Returns the greater of two {@code Instant} values. That is, the result is the argument closer to the value of
  146.      * {@link Instant#MAX}. If the arguments have the same value, the result is that same value.
  147.      *
  148.      * @param a an argument.
  149.      * @param b another argument.
  150.      * @return the larger of {@code a} and {@code b}.
  151.      */
  152.     static Instant max(final Instant a, final Instant b) {
  153.         return a.compareTo(b) > 0 ? a : b;
  154.     }

  155.     /**
  156.      * Returns the smaller of two {@code Instant} values. That is, the result is the argument closer to the value of
  157.      * {@link Instant#MIN}. If the arguments have the same value, the result is that same value.
  158.      *
  159.      * @param a an argument.
  160.      * @param b another argument.
  161.      * @return the smaller of {@code a} and {@code b}.
  162.      */
  163.     static Instant min(final Instant a, final Instant b) {
  164.         return a.compareTo(b) < 0 ? a : b;
  165.     }

  166.     /**
  167.      * Returns a non-null duration, value if non-null, otherwise defaultValue.
  168.      *
  169.      * @param value May be null.
  170.      * @param defaultValue May not be null/
  171.      * @return value if non-null, otherwise defaultValue.
  172.      */
  173.     static Duration nonNull(final Duration value, final Duration defaultValue) {
  174.         return value != null ? value : Objects.requireNonNull(defaultValue, "defaultValue");
  175.     }

  176.     /**
  177.      * Converts a {@link TimeUnit} to a {@link ChronoUnit}.
  178.      *
  179.      * @param timeUnit A TimeUnit.
  180.      * @return The corresponding ChronoUnit.
  181.      */
  182.     static ChronoUnit toChronoUnit(final TimeUnit timeUnit) {
  183.         // TODO when using Java >= 9: Use TimeUnit.toChronoUnit().
  184.         switch (Objects.requireNonNull(timeUnit)) {
  185.         case NANOSECONDS:
  186.             return ChronoUnit.NANOS;
  187.         case MICROSECONDS:
  188.             return ChronoUnit.MICROS;
  189.         case MILLISECONDS:
  190.             return ChronoUnit.MILLIS;
  191.         case SECONDS:
  192.             return ChronoUnit.SECONDS;
  193.         case MINUTES:
  194.             return ChronoUnit.MINUTES;
  195.         case HOURS:
  196.             return ChronoUnit.HOURS;
  197.         case DAYS:
  198.             return ChronoUnit.DAYS;
  199.         default:
  200.             throw new IllegalArgumentException(timeUnit.toString());
  201.         }
  202.     }

  203.     /**
  204.      * Converts am amount and TimeUnit into a Duration.
  205.      *
  206.      * @param amount the amount of the duration, measured in terms of the unit, positive or negative
  207.      * @param timeUnit the unit that the duration is measured in, must have an exact duration, not null
  208.      * @return a Duration.
  209.      */
  210.     static Duration toDuration(final long amount, final TimeUnit timeUnit) {
  211.         return Duration.of(amount, toChronoUnit(timeUnit));
  212.     }

  213. }