All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jdbcruntime.service.JdbcAdminServiceImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2022 Contributors to the Eclipse Foundation
 * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.jdbcruntime.service;

import com.sun.appserv.connectors.internal.api.ConnectorConstants;
import com.sun.enterprise.connectors.ConnectorConnectionPool;
import com.sun.enterprise.connectors.ConnectorRuntime;
import com.sun.enterprise.connectors.service.ConnectorAdminServiceUtils;
import com.sun.enterprise.connectors.service.ConnectorAdminServicesFactory;
import com.sun.enterprise.connectors.service.ConnectorConnectionPoolAdminServiceImpl;
import com.sun.enterprise.connectors.service.ConnectorService;
import com.sun.enterprise.connectors.util.DriverLoader;

import jakarta.inject.Singleton;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.ManagedConnection;
import jakarta.resource.spi.ManagedConnectionFactory;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;

import javax.naming.NamingException;

import org.glassfish.api.naming.SimpleJndiName;
import org.glassfish.resourcebase.resources.api.PoolInfo;
import org.jvnet.hk2.annotations.Service;

import static com.sun.enterprise.connectors.util.ConnectionPoolObjectsUtils.getValueFromMCF;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.WARNING;

/**
 * Jdbc admin service performs Jdbc related operations for administration.
 *
 * @author shalini
 */
@Service
@Singleton
public class JdbcAdminServiceImpl extends ConnectorService {

    private final ConnectorConnectionPoolAdminServiceImpl ccPoolAdmService;
    private static final String DBVENDOR_MAPPINGS_ROOT =
        System.getProperty(ConnectorConstants.INSTALL_ROOT) + File.separator +
        "lib" + File.separator +
        "install" + File.separator +
        "databases" + File.separator +
        "dbvendormapping" + File.separator;

    private final static String JDBC40_CONNECTION_VALIDATION = "org.glassfish.api.jdbc.validation.JDBC40ConnectionValidation";
    private final static String DS_PROPERTIES = "ds.properties";
    private final static String CPDS_PROPERTIES = "cpds.properties";
    private final static String XADS_PROPERTIES = "xads.properties";
    private final static String DRIVER_PROPERTIES = "driver.properties";
    private final static String CONVAL_PROPERTIES = "validationclassnames.properties";

    private static JdbcAdminServiceImpl jdbcAdminService = new JdbcAdminServiceImpl();

    /**
     * Default constructor
     */
    public JdbcAdminServiceImpl() {
        super();
        ccPoolAdmService = (ConnectorConnectionPoolAdminServiceImpl) ConnectorAdminServicesFactory.getService(ConnectorConstants.CCP);
    }

    public static JdbcAdminServiceImpl getJdbcAdminService() {
        if (jdbcAdminService == null) {
            throw new RuntimeException("JDBC admin service not initialized");
        }

        return jdbcAdminService;
    }

    /**
     * Get Validation class names list for the classname that the jdbc connection
     * pool refers to. This is used for custom connection validation.
     *
     * @param className
     * @return all validation class names.
     */
    public Set getValidationClassNames(String className) {
        SortedSet classNames = new TreeSet();
        if (className == null) {
            _logger.log(WARNING, "jdbc.admin.service.ds_class_name_null");
            return classNames;
        }
        File validationClassMappingFile;
        String dbVendor = getDatabaseVendorName(className);

        // Retrieve validation classnames from the properties file based on the
        // retrieved
        // dbvendor name
        if (dbVendor != null) {
            validationClassMappingFile = new File(DBVENDOR_MAPPINGS_ROOT + CONVAL_PROPERTIES);
            Properties validationClassMappings = DriverLoader.loadFile(validationClassMappingFile);
            String validationClassName = validationClassMappings.getProperty(dbVendor);
            if (validationClassName != null) {
                classNames.add(validationClassName);
            }
            // If JDBC40 runtime, add the jdbc40 validation classname
            if (detectJDBC40(className)) {
                classNames.add(JDBC40_CONNECTION_VALIDATION);
            }
        }
        return classNames;
    }

    private String getDatabaseVendorName(String className) {
        String dbVendor = getDatabaseVendorName(DriverLoader.loadFile(new File(DBVENDOR_MAPPINGS_ROOT + DS_PROPERTIES)), className);
        if (dbVendor == null) {
            dbVendor = getDatabaseVendorName(DriverLoader.loadFile(new File(DBVENDOR_MAPPINGS_ROOT + CPDS_PROPERTIES)), className);
        }
        if (dbVendor == null) {
            dbVendor = getDatabaseVendorName(DriverLoader.loadFile(new File(DBVENDOR_MAPPINGS_ROOT + XADS_PROPERTIES)), className);
        }
        if (dbVendor == null) {
            dbVendor = getDatabaseVendorName(DriverLoader.loadFile(new File(DBVENDOR_MAPPINGS_ROOT + DRIVER_PROPERTIES)), className);
        }
        return dbVendor;
    }

    private String getDatabaseVendorName(Properties classNameProperties, String className) {
        String dbVendor = null;
        Enumeration e = classNameProperties.propertyNames();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            String value = classNameProperties.getProperty(key);
            if (className.equalsIgnoreCase(value)) {
                // There could be multiple keys for a particular value.
                dbVendor = key;
                break;
            }
        }
        return dbVendor;
    }

    private boolean detectJDBC40(String className) {
        boolean jdbc40 = true;
        ClassLoader commonClassLoader = ConnectorRuntime.getRuntime().getClassLoaderHierarchy().getCommonClassLoader();
        Class cls = null;
        try {
            cls = commonClassLoader.loadClass(className);
        } catch (ClassNotFoundException e) {
            if (_logger.isLoggable(FINEST)) {
                _logger.log(FINEST, "jdbc.admin.service.ex_detect_jdbc40");
            }
            return false;
        }
        Method method;
        try {
            method = cls.getMethod("isWrapperFor", Class.class);
            method.invoke(cls.newInstance(), javax.sql.DataSource.class);
        } catch (NoSuchMethodException e) {
            jdbc40 = false;
            if (_logger.isLoggable(FINEST)) {
                _logger.log(FINEST, "jdbc.admin.service.ex_detect_jdbc40");
            }
        } catch (InvocationTargetException e) {
            if (e.getCause() instanceof AbstractMethodError) {
                jdbc40 = false;
            }
        } catch (InstantiationException e) {
            if (_logger.isLoggable(FINEST)) {
                _logger.log(FINEST, "jdbc.admin.service.ex_detect_jdbc40");
            }
            jdbc40 = false;
        } catch (IllegalAccessException e) {
            if (_logger.isLoggable(FINEST)) {
                _logger.log(FINEST, "jdbc.admin.service.ex_detect_jdbc40");
            }
            jdbc40 = false;
        }
        return jdbc40;
    }

    /**
     * Get Validation table names list for the database that the jdbc connection
     * pool refers to. This is used for connection validation.
     *
     * @param poolInfo
     * @return all validation table names.
     * @throws ResourceException
     */
    public Set getValidationTableNames(PoolInfo poolInfo) throws ResourceException {
        ManagedConnectionFactory managedConnectionFactory = null;
        ManagedConnection managedConnection = null;
        Connection connection = null;
        try {
            managedConnection = (ManagedConnection) ccPoolAdmService.getUnpooledConnection(poolInfo, null, false);
            managedConnectionFactory = ccPoolAdmService.obtainManagedConnectionFactory(poolInfo);

            if (managedConnection != null) {
                connection = (Connection) managedConnection.getConnection(null, null);
            }
            return getValidationTableNames(connection, getDefaultDatabaseName(poolInfo, managedConnectionFactory));
        } catch (Exception re) {
            _logger.log(WARNING, "pool.get_validation_table_names_failure", re.getMessage());
            throw new ResourceException(re);
        } finally {
            try {
                if (managedConnection != null) {
                    managedConnection.destroy();
                }
            } catch (Exception ex) {
                _logger.log(FINEST, "pool.get_validation_table_names_mc_destroy", poolInfo);
            }
        }
    }

    /**
     * Returns a databaseName that is populated in pool's default DATABASENAME
     *
     * @param poolInfo
     * @param managedConnectionFactory
     * @return
     * @throws javax.naming.NamingException if poolName lookup fails
     */
    private String getDefaultDatabaseName(PoolInfo poolInfo, ManagedConnectionFactory managedConnectionFactory) throws NamingException {
        // All this to get the default user name and principal
        String databaseName = null;
        ConnectorConnectionPool connectorConnectionPool = null;
        try {
            SimpleJndiName jndiNameForPool = ConnectorAdminServiceUtils.getReservePrefixedJNDINameForPool(poolInfo);
            connectorConnectionPool = (ConnectorConnectionPool) _runtime.getResourceNamingService().lookup(poolInfo, jndiNameForPool, null);
        } catch (NamingException ne) {
            throw ne;
        }

        databaseName = ccPoolAdmService.getPropertyValue("DATABASENAME", connectorConnectionPool);

        // To avoid using "" as the default databasename, try to get
        // the databasename from MCF.
        if (databaseName == null || databaseName.trim().equals("")) {
            databaseName = getValueFromMCF("DatabaseName", poolInfo, managedConnectionFactory);
        }

        return databaseName;
    }

    /**
     * Get Validation table names list for the catalog that the jdbc connection pool
     * refers to. This is used for connection validation.
     *
     * @param con
     * @param catalog database name used.
     * @return
     * @throws jakarta.resource.ResourceException
     */
    public static Set getValidationTableNames(java.sql.Connection con, String catalog) throws ResourceException {

        SortedSet tableNames = new TreeSet();
        if (catalog.trim().equals("")) {
            catalog = null;
        }

        if (con != null) {
            java.sql.ResultSet rs = null;
            try {
                DatabaseMetaData dmd = con.getMetaData();
                rs = dmd.getTables(catalog, null, null, null);
                while (rs.next()) {
                    String schemaName = rs.getString(2);
                    String tableName = rs.getString(3);
                    String actualTableName = tableName;
                    if (schemaName != null && !schemaName.equals("")) {
                        actualTableName = schemaName + "." + tableName;
                    }
                    tableNames.add(actualTableName);
                }
            } catch (Exception sqle) {
                _logger.log(Level.INFO, "pool.get_validation_table_names");
                throw new ResourceException(sqle);
            } finally {
                try {
                    if (rs != null) {
                        rs.close();
                    }
                } catch (Exception e1) {
                }
            }
        } else {
            throw new ResourceException("The connection is not valid as " + "the connection is null");
        }
        return tableNames;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy