org.glassfish.jdbcruntime.service.JdbcAdminServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016-2021] [Payara Foundation]
package org.glassfish.jdbcruntime.service;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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 jakarta.inject.Singleton;
import javax.naming.NamingException;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.ManagedConnection;
import jakarta.resource.spi.ManagedConnectionFactory;
import org.glassfish.resourcebase.resources.api.PoolInfo;
import org.jvnet.hk2.annotations.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.ConnectionPoolObjectsUtils;
import com.sun.enterprise.connectors.util.DriverLoader;
/**
* Jdbc admin service performs Jdbc related operations for administration.
*
* @author shalini
*/
@Service
@Singleton
public class JdbcAdminServiceImpl extends ConnectorService {
private 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();
//jdbcAdminService = this;
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(Level.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(Level.FINEST)) {
_logger.log(Level.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(Level.FINEST)) {
_logger.log(Level.FINEST, "jdbc.admin.service.ex_detect_jdbc40");
}
} catch (InvocationTargetException e) {
if(e.getCause() instanceof AbstractMethodError) {
jdbc40 = false;
}
} catch (InstantiationException e) {
if(_logger.isLoggable(Level.FINEST)) {
_logger.log(Level.FINEST, "jdbc.admin.service.ex_detect_jdbc40");
}
jdbc40 = false;
} catch (IllegalAccessException e) {
if(_logger.isLoggable(Level.FINEST)) {
_logger.log(Level.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 jakarta.resource.ResourceException
* @throws javax.naming.NamingException
*/
public Set getValidationTableNames(PoolInfo poolInfo)
throws ResourceException {
ManagedConnectionFactory mcf = null;
ManagedConnection mc = null;
java.sql.Connection con = null;
try {
mc = (ManagedConnection) ccPoolAdmService.getUnpooledConnection(poolInfo, null, false);
mcf = ccPoolAdmService.obtainManagedConnectionFactory(poolInfo);
if (mc != null) {
con = (java.sql.Connection) mc.getConnection(null, null);
}
return getValidationTableNames(con,
getDefaultDatabaseName(poolInfo, mcf));
} catch(Exception re) {
_logger.log(Level.WARNING, "pool.get_validation_table_names_failure", re.getMessage());
throw new ResourceException(re);
} finally {
try {
if(mc != null) {
mc.destroy();
}
} catch(Exception ex) {
if(_logger.isLoggable(Level.FINEST)) {
_logger.log(Level.FINEST, "pool.get_validation_table_names_mc_destroy", poolInfo);
}
}
}
}
/**
* Returns a databaseName that is populated in pool's default DATABASENAME
* @param poolInfo
* @param mcf
* @return
* @throws javax.naming.NamingException if poolName lookup fails
*/
private String getDefaultDatabaseName(PoolInfo poolInfo, ManagedConnectionFactory mcf)
throws NamingException {
// All this to get the default user name and principal
String databaseName = null;
ConnectorConnectionPool connectorConnectionPool = null;
try {
String 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 = ConnectionPoolObjectsUtils.getValueFromMCF("DatabaseName", poolInfo, mcf);
}
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;
}
/**
* Utility method to check if the retrieved table is accessible, since this
* will be used for connection validation method "table".
* @param tableName
* @param con
* @return accessibility status of the table.
*/
/*private static boolean isPingable(String tableName, java.sql.Connection con) {
java.sql.PreparedStatement stmt = null;
java.sql.ResultSet rs = null;
final String sql = "SELECT COUNT(*) FROM " + tableName;
try {
stmt = con.prepareStatement(sql);
rs = stmt.executeQuery();
} catch (Exception sqle) {
_logger.log(Level.INFO, "pool.exc_is_pingable", tableName);
return false;
} finally {
try {
if (rs != null) {
rs.close();
}
} catch (Exception e1) {
}
try {
if (stmt != null) {
stmt.close();
}
} catch (Exception e2) {
}
}
return true;
} */
}