Utils.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.dbcp2;

  18. import java.sql.Connection;
  19. import java.sql.ResultSet;
  20. import java.sql.Statement;
  21. import java.text.MessageFormat;
  22. import java.time.Duration;
  23. import java.time.Instant;
  24. import java.util.Collection;
  25. import java.util.HashSet;
  26. import java.util.Properties;
  27. import java.util.ResourceBundle;
  28. import java.util.Set;
  29. import java.util.function.Consumer;

  30. import org.apache.commons.pool2.PooledObject;

  31. /**
  32.  * Utility methods.
  33.  *
  34.  * @since 2.0
  35.  */
  36. public final class Utils {

  37.     private static final ResourceBundle messages = ResourceBundle
  38.         .getBundle(Utils.class.getPackage().getName() + ".LocalStrings");

  39.     /**
  40.      * Whether the security manager is enabled.
  41.      *
  42.      * @deprecated No replacement.
  43.      */
  44.     @Deprecated
  45.     public static final boolean IS_SECURITY_ENABLED = isSecurityEnabled();

  46.     /**
  47.      * Any SQL State starting with this value is considered a fatal disconnect.
  48.      */
  49.     public static final String DISCONNECTION_SQL_CODE_PREFIX = "08";

  50.     /**
  51.      * SQL codes of fatal connection errors.
  52.      * <ul>
  53.      * <li>57P01 (Admin shutdown)</li>
  54.      * <li>57P02 (Crash shutdown)</li>
  55.      * <li>57P03 (Cannot connect now)</li>
  56.      * <li>01002 (SQL92 disconnect error)</li>
  57.      * <li>JZ0C0 (Sybase disconnect error)</li>
  58.      * <li>JZ0C1 (Sybase disconnect error)</li>
  59.      * </ul>
  60.      * @deprecated Use {@link #getDisconnectionSqlCodes()}.
  61.      */
  62.     @Deprecated
  63.     public static final Set<String> DISCONNECTION_SQL_CODES;

  64.     static final ResultSet[] EMPTY_RESULT_SET_ARRAY = {};

  65.     static final String[] EMPTY_STRING_ARRAY = {};
  66.     static {
  67.         DISCONNECTION_SQL_CODES = new HashSet<>();
  68.         DISCONNECTION_SQL_CODES.add("57P01"); // Admin shutdown
  69.         DISCONNECTION_SQL_CODES.add("57P02"); // Crash shutdown
  70.         DISCONNECTION_SQL_CODES.add("57P03"); // Cannot connect now
  71.         DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
  72.         DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
  73.         DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
  74.     }

  75.     /**
  76.      * Checks for conflicts between two collections.
  77.      * <p>
  78.      * If any overlap is found between the two provided collections, an {@link IllegalArgumentException} is thrown.
  79.      * </p>
  80.      *
  81.      * @param codes1 The first collection of SQL state codes.
  82.      * @param codes2 The second collection of SQL state codes.
  83.      * @throws IllegalArgumentException if any codes overlap between the two collections.
  84.      * @since 2.13.0
  85.      */
  86.     static void checkSqlCodes(final Collection<String> codes1, final Collection<String> codes2) {
  87.         if (codes1 != null && codes2 != null) {
  88.             final Set<String> test = new HashSet<>(codes1);
  89.             test.retainAll(codes2);
  90.             if (!test.isEmpty()) {
  91.                 throw new IllegalArgumentException(test + " cannot be in both disconnectionSqlCodes and disconnectionIgnoreSqlCodes.");
  92.             }
  93.         }
  94.     }

  95.     /**
  96.      * Clones the given char[] if not null.
  97.      *
  98.      * @param value may be null.
  99.      * @return a cloned char[] or null.
  100.      */
  101.     public static char[] clone(final char[] value) {
  102.         return value == null ? null : value.clone();
  103.     }

  104.     /**
  105.      * Clones the given {@link Properties} without the standard "user" or "password" entries.
  106.      *
  107.      * @param properties may be null
  108.      * @return a clone of the input without the standard "user" or "password" entries.
  109.      * @since 2.8.0
  110.      */
  111.     public static Properties cloneWithoutCredentials(final Properties properties) {
  112.         if (properties != null) {
  113.             final Properties temp = (Properties) properties.clone();
  114.             temp.remove(Constants.KEY_USER);
  115.             temp.remove(Constants.KEY_PASSWORD);
  116.             return temp;
  117.         }
  118.         return properties;
  119.     }

  120.     /**
  121.      * Closes the given {@link AutoCloseable} and if an exception is caught, then calls {@code exceptionHandler}.
  122.      *
  123.      * @param autoCloseable The resource to close.
  124.      * @param exceptionHandler Consumes exception thrown closing this resource.
  125.      * @since 2.10.0
  126.      */
  127.     public static void close(final AutoCloseable autoCloseable, final Consumer<Exception> exceptionHandler) {
  128.         if (autoCloseable != null) {
  129.             try {
  130.                 autoCloseable.close();
  131.             } catch (final Exception e) {
  132.                 if (exceptionHandler != null) {
  133.                     exceptionHandler.accept(e);
  134.                 }
  135.             }
  136.         }
  137.     }

  138.     /**
  139.      * Closes the AutoCloseable (which may be null).
  140.      *
  141.      * @param autoCloseable an AutoCloseable, may be {@code null}
  142.      * @since 2.6.0
  143.      */
  144.     public static void closeQuietly(final AutoCloseable autoCloseable) {
  145.         close(autoCloseable, null);
  146.     }

  147.     /**
  148.      * Closes the Connection (which may be null).
  149.      *
  150.      * @param connection a Connection, may be {@code null}
  151.      * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
  152.      */
  153.     @Deprecated
  154.     public static void closeQuietly(final Connection connection) {
  155.         closeQuietly((AutoCloseable) connection);
  156.     }

  157.     /**
  158.      * Closes the ResultSet (which may be null).
  159.      *
  160.      * @param resultSet a ResultSet, may be {@code null}
  161.      * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
  162.      */
  163.     @Deprecated
  164.     public static void closeQuietly(final ResultSet resultSet) {
  165.         closeQuietly((AutoCloseable) resultSet);
  166.     }

  167.     /**
  168.      * Closes the Statement (which may be null).
  169.      *
  170.      * @param statement a Statement, may be {@code null}.
  171.      * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
  172.      */
  173.     @Deprecated
  174.     public static void closeQuietly(final Statement statement) {
  175.         closeQuietly((AutoCloseable) statement);
  176.     }

  177.     /**
  178.      * Gets a copy of SQL codes of fatal connection errors.
  179.      * <ul>
  180.      * <li>57P01 (Admin shutdown)</li>
  181.      * <li>57P02 (Crash shutdown)</li>
  182.      * <li>57P03 (Cannot connect now)</li>
  183.      * <li>01002 (SQL92 disconnect error)</li>
  184.      * <li>JZ0C0 (Sybase disconnect error)</li>
  185.      * <li>JZ0C1 (Sybase disconnect error)</li>
  186.      * </ul>
  187.      * @return A copy SQL codes of fatal connection errors.
  188.      * @since 2.10.0
  189.      */
  190.     public static Set<String> getDisconnectionSqlCodes() {
  191.         return new HashSet<>(DISCONNECTION_SQL_CODES);
  192.     }

  193.     /**
  194.      * Gets the correct i18n message for the given key.
  195.      *
  196.      * @param key The key to look up an i18n message.
  197.      * @return The i18n message.
  198.      */
  199.     public static String getMessage(final String key) {
  200.         return getMessage(key, (Object[]) null);
  201.     }

  202.     /**
  203.      * Gets the correct i18n message for the given key with placeholders replaced by the supplied arguments.
  204.      *
  205.      * @param key A message key.
  206.      * @param args The message arguments.
  207.      * @return An i18n message.
  208.      */
  209.     public static String getMessage(final String key, final Object... args) {
  210.         final String msg = messages.getString(key);
  211.         if (args == null || args.length == 0) {
  212.             return msg;
  213.         }
  214.         final MessageFormat mf = new MessageFormat(msg);
  215.         return mf.format(args, new StringBuffer(), null).toString();
  216.     }

  217.     /**
  218.      * Checks if the given SQL state corresponds to a fatal connection error.
  219.      *
  220.      * @param sqlState the SQL state to check.
  221.      * @return true if the SQL state is a fatal connection error, false otherwise.
  222.      * @since 2.13.0
  223.      */
  224.     static boolean isDisconnectionSqlCode(final String sqlState) {
  225.         return DISCONNECTION_SQL_CODES.contains(sqlState);
  226.     }

  227.     static boolean isEmpty(final Collection<?> collection) {
  228.         return collection == null || collection.isEmpty();
  229.     }

  230.     static boolean isSecurityEnabled() {
  231.         return System.getSecurityManager() != null;
  232.     }

  233.     /**
  234.      * Converts the given String to a char[].
  235.      *
  236.      * @param value may be null.
  237.      * @return a char[] or null.
  238.      */
  239.     public static char[] toCharArray(final String value) {
  240.         return value != null ? value.toCharArray() : null;
  241.     }

  242.     /**
  243.      * Converts the given char[] to a String.
  244.      *
  245.      * @param value may be null.
  246.      * @return a String or null.
  247.      */
  248.     public static String toString(final char[] value) {
  249.         return value == null ? null : String.valueOf(value);
  250.     }

  251.     /**
  252.      * Throws a LifetimeExceededException if the given pooled object's lifetime has exceeded a maximum duration.
  253.      *
  254.      * @param p           The pooled object to test.
  255.      * @param maxDuration The maximum lifetime.
  256.      * @throws LifetimeExceededException Thrown if the given pooled object's lifetime has exceeded a maximum duration.
  257.      */
  258.     public static void validateLifetime(final PooledObject<?> p, final Duration maxDuration) throws LifetimeExceededException {
  259.         if (maxDuration.compareTo(Duration.ZERO) > 0) {
  260.             final Duration lifetimeDuration = Duration.between(p.getCreateInstant(), Instant.now());
  261.             if (lifetimeDuration.compareTo(maxDuration) > 0) {
  262.                 throw new LifetimeExceededException(getMessage("connectionFactory.lifetimeExceeded", lifetimeDuration, maxDuration));
  263.             }
  264.         }
  265.     }

  266.     private Utils() {
  267.         // not instantiable
  268.     }

  269. }