Skip to content

Commit

Permalink
Merge pull request #25088 from OndroMih/ondromih-fix-sql-tracing
Browse files Browse the repository at this point in the history
Improvements and fixes in the SQL trace listener functionality
  • Loading branch information
hs536 authored Aug 21, 2024
2 parents f39ca94 + 3ebd319 commit 0c8a14d
Show file tree
Hide file tree
Showing 14 changed files with 765 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.logging.LogDomains;

import jakarta.inject.Inject;
import jakarta.resource.spi.ManagedConnectionFactory;

import java.util.Set;
Expand All @@ -41,6 +42,7 @@

import org.glassfish.api.naming.SimpleJndiName;
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.resourcebase.resources.api.PoolInfo;
import org.glassfish.resourcebase.resources.api.ResourceInfo;
import org.jvnet.hk2.annotations.Service;
Expand All @@ -64,13 +66,15 @@ public class ActiveResourceAdapterImpl implements ActiveResourceAdapter {
private static Logger _logger = LogDomains.getLogger(ActiveResourceAdapterImpl.class, LogDomains.RSR_LOGGER);
private final StringManager localStrings = StringManager.getManager(ActiveResourceAdapterImpl.class);

@Inject
protected ServiceLocator locator;

protected ConnectorDescriptor desc_;
protected String moduleName_;
protected ClassLoader jcl_;
protected ConnectionDefDescriptor[] connectionDefs_;
protected ConnectorRuntime connectorRuntime_;


/**
* Constructor.
*
Expand Down Expand Up @@ -385,17 +389,18 @@ private void setLogWriter(ManagedConnectionFactory mcf) {
}


protected ManagedConnectionFactory instantiateMCF(String mcfClass, ClassLoader loader) throws Exception {
protected ManagedConnectionFactory instantiateMCF(String mcfClassName, ClassLoader loader) throws Exception {
ManagedConnectionFactory mcf = null;
Class<?> mcfClass;
if (jcl_ != null) {
mcf = (ManagedConnectionFactory) jcl_.loadClass(mcfClass).getDeclaredConstructor().newInstance();
mcfClass = jcl_.loadClass(mcfClassName);
} else if (loader != null) {
mcf = (ManagedConnectionFactory) loader.loadClass(mcfClass).getDeclaredConstructor().newInstance();
mcfClass = loader.loadClass(mcfClassName);

} else {
// mcf = (ManagedConnectionFactory) Class.forName(mcfClass).newInstance();
mcf = (ManagedConnectionFactory) Thread.currentThread().getContextClassLoader().loadClass(mcfClass)
.getDeclaredConstructor().newInstance();
mcfClass = Thread.currentThread().getContextClassLoader().loadClass(mcfClassName);
}
mcf = locator.createAndInitialize((Class<ManagedConnectionFactory>)mcfClass);
setLogWriter(mcf);
return mcf;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2024 Eclipse Foundation 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.main.itest.tools.asadmin;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import static org.glassfish.main.itest.tools.asadmin.AsadminResultMatcher.asadminOK;
import static org.hamcrest.MatcherAssert.assertThat;

/**
*
* @author Ondro Mihalyi
*/
public class DomainSettings {

private final Asadmin asadmin;
List<String> settingsBackup = new ArrayList<>();

public DomainSettings(Asadmin asadmin) {
this.asadmin = asadmin;
}

public void backupSettings(String getKey) {
final AsadminResult result = asadmin.exec(5_000, "get", getKey);
Stream.of(result.getStdOut().split("\n"))
// Exclude "command successful
.filter(line -> line.contains("="))
// Exclude .name for connection pools which cannot be changed
.filter(line -> !line.startsWith("resources.jdbc-connection-pool") || !line.contains(".name="))
.forEach(settingsBackup::add);
}

public void restoreSettings() {
String[] args = new String[settingsBackup.size() + 1];
args[0] = "set";
for (int i = 1; i < args.length; i++) {
args[i] = settingsBackup.get(i - 1);
}
settingsBackup.clear();
final AsadminResult result = asadmin.exec(5_000, args);
assertThat(result, asadminOK());
}

public void backupDerbyPoolSettings() {
backupSettings("resources.jdbc-connection-pool.DerbyPool.*");
}

/** Default is org.apache.derby.jdbc.ClientDataSource */
public void setDerbyPoolEmbededded() {
final AsadminResult result = asadmin.exec(5_000, "set",
"resources.jdbc-connection-pool.DerbyPool.datasource-classname=org.apache.derby.jdbc.EmbeddedDataSource",
"resources.jdbc-connection-pool.DerbyPool.property.PortNumber=",
"resources.jdbc-connection-pool.DerbyPool.property.serverName=",
"resources.jdbc-connection-pool.DerbyPool.property.URL=");
assertThat(result, asadminOK());
asadmin.exec(5_000, "get", "resources.jdbc-connection-pool.DerbyPool.*");
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.sun.gjc.util.SQLTraceDelegator;
import com.sun.logging.LogDomains;

import jakarta.inject.Inject;
import jakarta.resource.ResourceException;
import jakarta.resource.spi.ConfigProperty;
import jakarta.resource.spi.ConnectionManager;
Expand Down Expand Up @@ -65,6 +66,7 @@
import javax.sql.DataSource;
import javax.sql.PooledConnection;

import org.glassfish.api.invocation.InvocationManager;
import org.glassfish.api.jdbc.ConnectionValidation;
import org.glassfish.api.jdbc.SQLTraceListener;
import org.glassfish.api.jdbc.objects.TxIsolationLevel;
Expand Down Expand Up @@ -101,6 +103,9 @@ public abstract class ManagedConnectionFactoryImpl
private static Logger _logger = LogDomains.getLogger(ManagedConnectionFactoryImpl.class, LogDomains.RSR_LOGGER);
protected static final StringManager localStrings = StringManager.getManager(DataSourceObjectBuilder.class);

@Inject
protected InvocationManager invocationManager;

protected DataSourceSpec spec = new DataSourceSpec();
protected transient DataSourceObjectBuilder dataSourceObjectBuilder;
protected PrintWriter logWriter;
Expand Down Expand Up @@ -553,7 +558,7 @@ private void detectSqlTraceListeners() {
String delimiter = ",";

if (sqlTraceListeners != null && !sqlTraceListeners.equals("null")) {
sqlTraceDelegator = new SQLTraceDelegator(getPoolName(), getApplicationName(), getModuleName());
sqlTraceDelegator = new SQLTraceDelegator(getPoolName(), invocationManager);
StringTokenizer st = new StringTokenizer(sqlTraceListeners, delimiter);

while (st.hasMoreTokens()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2022,2024 Contributors to the Eclipse Foundation
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand All @@ -14,17 +14,21 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/

package com.sun.gjc.util;

import com.sun.gjc.monitoring.JdbcRAConstants;
import com.sun.gjc.monitoring.SQLTraceProbeProvider;
import com.sun.logging.LogDomains;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;

import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.api.invocation.InvocationManager;
import org.glassfish.api.jdbc.SQLTraceListener;
import org.glassfish.api.jdbc.SQLTraceRecord;
import org.glassfish.api.naming.SimpleJndiName;
Expand All @@ -45,24 +49,21 @@ public class SQLTraceDelegator implements SQLTraceListener {
// List of listeners
protected List<SQLTraceListener> sqlTraceListenersList;
private final SimpleJndiName poolName;
private final String appName;
private final String moduleName;
private InvocationManager invocationManager;
private SQLTraceProbeProvider probeProvider = null;

public SQLTraceProbeProvider getProbeProvider() {
return probeProvider;
}

public SQLTraceDelegator(SimpleJndiName poolName, String appName, String moduleName) {
public SQLTraceDelegator(SimpleJndiName poolName, InvocationManager invocationManager) {
this.poolName = poolName;
this.appName = appName;
this.moduleName = moduleName;
this.invocationManager = invocationManager;
probeProvider = new SQLTraceProbeProvider();
}

/**
* Add a listener to the list of sql trace listeners maintained by this
* registry.
* Add a listener to the list of sql trace listeners maintained by this registry.
*
* @param listener
*/
Expand All @@ -75,55 +76,108 @@ public void registerSQLTraceListener(SQLTraceListener listener) {

@Override
public void sqlTrace(SQLTraceRecord record) {
if (sqlTraceListenersList != null) {
for (SQLTraceListener listener : sqlTraceListenersList) {
try {
listener.sqlTrace(record);
} catch (Exception e) {
// it is possible that any of the implementations may fail processing a trace
// record.
// do not propagate such failures. Log them as FINEST.
if (_logger.isLoggable(FINEST)) {
_logger.log(FINEST,
"exception from one of the SQL trace listeners [" + listener.getClass().getName() + "]",
e);
if (record != null) {
record.setPoolName(poolName.toString());

String sqlQuery = findSqlQuery(record);
record.setSqlQuery(sqlQuery);
record.setApplicationName(getAppName());
record.setModuleName(getModuleName());

if (sqlQuery != null) {
probeProvider.traceSQLEvent(poolName.toString(), record.getApplicationName(),
record.getModuleName(), sqlQuery);
}

if (sqlTraceListenersList != null && !sqlTraceListenersList.isEmpty()) {
getCallingApplicationStackFrame().ifPresent(record::setCallingApplicationMethod);
for (SQLTraceListener listener : sqlTraceListenersList) {
try {
listener.sqlTrace(record);
} catch (Exception e) {
// it is possible that any of the implementations may fail processing a trace
// record.
// do not propagate such failures. Log them as FINEST.
if (_logger.isLoggable(FINEST)) {
_logger.log(FINEST,
"exception from one of the SQL trace listeners [" + listener.getClass().getName() + "]",
e);
}
}
}
}
}
}

if (record != null) {
record.setPoolName(poolName.toString());
String methodName = record.getMethodName();
// Check if the method name is one in which sql query is used
if (isMethodValidForCaching(methodName)) {
Object[] params = record.getParams();
if (params != null && params.length > 0) {
String sqlQuery = null;
for (Object param : params) {
if (param instanceof String) {
sqlQuery = param.toString();
}
break;
}
if (sqlQuery != null) {
probeProvider.traceSQLEvent(poolName.toString(), appName, moduleName, sqlQuery);
private Optional<StackWalker.StackFrame> getCallingApplicationStackFrame() {
Set<Class<?>> checkedClasses = new HashSet<>();
checkedClasses.add(this.getClass());
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
.walk(traces -> {
return traces
.filter(trace -> {
Class<?> declaringClass = trace.getDeclaringClass();
final ClassLoader appClassLoader = Thread.currentThread().getContextClassLoader();
boolean result = !checkedClasses.contains(declaringClass) && isClassFromApplication(declaringClass, appClassLoader);
if (!result) {
checkedClasses.add(declaringClass);
}
return result;
})
.findFirst();
});
}

private boolean isClassFromApplication(Class<?> cls, ClassLoader appClassLoader) {
ClassLoader clsClassLoader = cls.getClassLoader();
while (clsClassLoader != null) {
if (clsClassLoader.equals(appClassLoader)) {
return true;
}
clsClassLoader = clsClassLoader.getParent();
}
return false;
}

private String findSqlQuery(SQLTraceRecord record) {
// Check if the method name is one in which sql query is used
String methodName = record.getMethodName();
String sqlQuery = null;

if (isMethodValidForCaching(methodName)) {
Object[] params = record.getParams();
if (params != null && params.length > 0) {
for (Object param : params) {
if (param instanceof String) {
sqlQuery = param.toString();
}
break;
}
}
}
return sqlQuery;
}

private String getAppName() {
ComponentInvocation currentInvocation = invocationManager.getCurrentInvocation();
return currentInvocation != null ? currentInvocation.getAppName() : null;
}

private String getModuleName() {
ComponentInvocation currentInvocation = invocationManager.getCurrentInvocation();
return currentInvocation != null ? currentInvocation.getModuleName(): null;
}

/**
* Check if the method name from the sql trace record can be used to retrieve a
* sql string for caching purpose. Most of the method names do not contain a sql
* string and hence are unusable for caching the sql strings. These method names
* are filtered in this method.
* Check if the method name from the sql trace record can be used to retrieve a sql string for caching purpose. Most
* of the method names do not contain a sql string and hence are unusable for caching the sql strings. These method
* names are filtered in this method.
*
* @param methodName
* @return true if method name can be used to get a sql string for caching.
*/
private boolean isMethodValidForCaching(String methodName) {
return JdbcRAConstants.validSqlTracingMethodNames.contains(methodName);
}

}
8 changes: 7 additions & 1 deletion appserver/tests/application/pom.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2023 Contributors to the Eclipse Foundation. All rights reserved.
Copyright (c) 2023, 2024 Contributors to the Eclipse Foundation. 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
Expand Down Expand Up @@ -134,6 +134,12 @@
<artifactId>glassfish-corba-omgapi</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.main.common</groupId>
<artifactId>glassfish-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Loading

0 comments on commit 0c8a14d

Please sign in to comment.