    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *   http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.dbcp2;
    018
    019import java.sql.Connection;
    020import java.sql.ResultSet;
    021import java.sql.Statement;
    022import java.text.MessageFormat;
    023import java.time.Duration;
    024import java.time.Instant;
    025import java.util.Collection;
    026import java.util.HashSet;
    027import java.util.Properties;
    028import java.util.ResourceBundle;
    029import java.util.Set;
    030import java.util.function.Consumer;
    031
    032import org.apache.commons.pool2.PooledObject;
    033
    034/**
    035 * Utility methods.
    036 *
    037 * @since 2.0
    038 */
    039public final class Utils {
    040
    041    private static final ResourceBundle messages = ResourceBundle
    042        .getBundle(Utils.class.getPackage().getName() + ".LocalStrings");
    043
    044    /**
    045     * Whether the security manager is enabled.
    046     *
    047     * @deprecated No replacement.
    048     */
    049    @Deprecated
    050    public static final boolean IS_SECURITY_ENABLED = isSecurityEnabled();
    051
    052    /** Any SQL_STATE starting with this value is considered a fatal disconnect */
    053    public static final String DISCONNECTION_SQL_CODE_PREFIX = "08";
    054
    055    /**
    056     * SQL codes of fatal connection errors.
    057     * <ul>
    058     * <li>57P01 (Admin shutdown)</li>
    059     * <li>57P02 (Crash shutdown)</li>
    060     * <li>57P03 (Cannot connect now)</li>
    061     * <li>01002 (SQL92 disconnect error)</li>
    062     * <li>JZ0C0 (Sybase disconnect error)</li>
    063     * <li>JZ0C1 (Sybase disconnect error)</li>
    064     * </ul>
    065     * @deprecated Use {@link #getDisconnectionSqlCodes()}.
    066     */
    067    @Deprecated
    068    public static final Set<String> DISCONNECTION_SQL_CODES;
    069
    070    static final ResultSet[] EMPTY_RESULT_SET_ARRAY = {};
    071
    072    static final String[] EMPTY_STRING_ARRAY = {};
    073    static {
    074        DISCONNECTION_SQL_CODES = new HashSet<>();
    075        DISCONNECTION_SQL_CODES.add("57P01"); // Admin shutdown
    076        DISCONNECTION_SQL_CODES.add("57P02"); // Crash shutdown
    077        DISCONNECTION_SQL_CODES.add("57P03"); // Cannot connect now
    078        DISCONNECTION_SQL_CODES.add("01002"); // SQL92 disconnect error
    079        DISCONNECTION_SQL_CODES.add("JZ0C0"); // Sybase disconnect error
    080        DISCONNECTION_SQL_CODES.add("JZ0C1"); // Sybase disconnect error
    081    }
    082
    083    /**
    084     * Clones the given char[] if not null.
    085     *
    086     * @param value may be null.
    087     * @return a cloned char[] or null.
    088     */
    089    public static char[] clone(final char[] value) {
    090        return value == null ? null : value.clone();
    091    }
    092
    093    /**
    094     * Clones the given {@link Properties} without the standard "user" or "password" entries.
    095     *
    096     * @param properties may be null
    097     * @return a clone of the input without the standard "user" or "password" entries.
    098     * @since 2.8.0
    099     */
    100    public static Properties cloneWithoutCredentials(final Properties properties) {
    101        if (properties != null) {
    102            final Properties temp = (Properties) properties.clone();
    103            temp.remove(Constants.KEY_USER);
    104            temp.remove(Constants.KEY_PASSWORD);
    105            return temp;
    106        }
    107        return properties;
    108    }
    109
    110    /**
    111     * Closes the given {@link AutoCloseable} and if an exception is caught, then calls {@code exceptionHandler}.
    112     *
    113     * @param autoCloseable The resource to close.
    114     * @param exceptionHandler Consumes exception thrown closing this resource.
    115     * @since 2.10.0
    116     */
    117    public static void close(final AutoCloseable autoCloseable, final Consumer<Exception> exceptionHandler) {
    118        if (autoCloseable != null) {
    119            try {
    120                autoCloseable.close();
    121            } catch (final Exception e) {
    122                if (exceptionHandler != null) {
    123                    exceptionHandler.accept(e);
    124                }
    125            }
    126        }
    127    }
    128
    129    /**
    130     * Closes the AutoCloseable (which may be null).
    131     *
    132     * @param autoCloseable an AutoCloseable, may be {@code null}
    133     * @since 2.6.0
    134     */
    135    public static void closeQuietly(final AutoCloseable autoCloseable) {
    136        close(autoCloseable, null);
    137    }
    138
    139    /**
    140     * Closes the Connection (which may be null).
    141     *
    142     * @param connection a Connection, may be {@code null}
    143     * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
    144     */
    145    @Deprecated
    146    public static void closeQuietly(final Connection connection) {
    147        closeQuietly((AutoCloseable) connection);
    148    }
    149
    150    /**
    151     * Closes the ResultSet (which may be null).
    152     *
    153     * @param resultSet a ResultSet, may be {@code null}
    154     * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
    155     */
    156    @Deprecated
    157    public static void closeQuietly(final ResultSet resultSet) {
    158        closeQuietly((AutoCloseable) resultSet);
    159    }
    160
    161    /**
    162     * Closes the Statement (which may be null).
    163     *
    164     * @param statement a Statement, may be {@code null}.
    165     * @deprecated Use {@link #closeQuietly(AutoCloseable)}.
    166     */
    167    @Deprecated
    168    public static void closeQuietly(final Statement statement) {
    169        closeQuietly((AutoCloseable) statement);
    170    }
    171
    172    /**
    173     * Gets a copy of SQL codes of fatal connection errors.
    174     * <ul>
    175     * <li>57P01 (Admin shutdown)</li>
    176     * <li>57P02 (Crash shutdown)</li>
    177     * <li>57P03 (Cannot connect now)</li>
    178     * <li>01002 (SQL92 disconnect error)</li>
    179     * <li>JZ0C0 (Sybase disconnect error)</li>
    180     * <li>JZ0C1 (Sybase disconnect error)</li>
    181     * </ul>
    182     * @return SQL codes of fatal connection errors.
    183     * @since 2.10.0
    184     */
    185    public static Set<String> getDisconnectionSqlCodes() {
    186        return new HashSet<>(DISCONNECTION_SQL_CODES);
    187    }
    188
    189    /**
    190     * Gets the correct i18n message for the given key.
    191     *
    192     * @param key The key to look up an i18n message.
    193     * @return The i18n message.
    194     */
    195    public static String getMessage(final String key) {
    196        return getMessage(key, (Object[]) null);
    197    }
    198
    199    /**
    200     * Gets the correct i18n message for the given key with placeholders replaced by the supplied arguments.
    201     *
    202     * @param key A message key.
    203     * @param args The message arguments.
    204     * @return An i18n message.
    205     */
    206    public static String getMessage(final String key, final Object... args) {
    207        final String msg = messages.getString(key);
    208        if (args == null || args.length == 0) {
    209            return msg;
    210        }
    211        final MessageFormat mf = new MessageFormat(msg);
    212        return mf.format(args, new StringBuffer(), null).toString();
    213    }
    214
    215    static boolean isEmpty(final Collection<?> collection) {
    216        return collection == null || collection.isEmpty();
    217    }
    218
    219    static boolean isSecurityEnabled() {
    220        return System.getSecurityManager() != null;
    221    }
    222
    223    /**
    224     * Converts the given String to a char[].
    225     *
    226     * @param value may be null.
    227     * @return a char[] or null.
    228     */
    229    public static char[] toCharArray(final String value) {
    230        return value != null ? value.toCharArray() : null;
    231    }
    232
    233    /**
    234     * Converts the given char[] to a String.
    235     *
    236     * @param value may be null.
    237     * @return a String or null.
    238     */
    239    public static String toString(final char[] value) {
    240        return value == null ? null : String.valueOf(value);
    241    }
    242
    243    public static void validateLifetime(final PooledObject<?> p, final Duration maxDuration) throws LifetimeExceededException {
    244        if (maxDuration.compareTo(Duration.ZERO) > 0) {
    245            final Duration lifetimeDuration = Duration.between(p.getCreateInstant(), Instant.now());
    246            if (lifetimeDuration.compareTo(maxDuration) > 0) {
    247                throw new LifetimeExceededException(Utils.getMessage("connectionFactory.lifetimeExceeded", lifetimeDuration, maxDuration));
    248            }
    249        }
    250    }
    251
    252    private Utils() {
    253        // not instantiable
    254    }
    255
    256}