blob: 47ede7c52e3b16201501b1dc07b84ad2c8ba00f4 [file] [log] [blame]
/*
* Copyright (c) 1997, 2018 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 com.sun.enterprise.resource.pool.monitor;
import com.sun.appserv.connectors.internal.api.ConnectorsUtil;
import com.sun.enterprise.config.serverbeans.ResourcePool;
import com.sun.enterprise.connectors.ConnectorRuntime;
import com.sun.enterprise.resource.listener.PoolLifeCycleListener;
import com.sun.logging.LogDomains;
import org.glassfish.connectors.config.ConnectorConnectionPool;
import org.glassfish.external.probe.provider.PluginPoint;
import org.glassfish.external.probe.provider.StatsProviderManager;
import org.glassfish.resourcebase.resources.api.PoolInfo;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Implementation of PoolLifeCycleListener interface to listen to events related
* to jdbc monitoring. The methods invoke the probe providers internally to
* provide the monitoring related information.
*
* @author Shalini M
*/
public class ConnectionPoolEmitterImpl implements PoolLifeCycleListener {
private String poolName;
private String appName;
private String moduleName;
private PoolInfo poolInfo;
private ConnectionPoolProbeProvider poolProbeProvider;
//Map of app names and respective emitters for a pool.
private Map<PoolInfo, Map<String, ConnectionPoolAppEmitterImpl>> appStatsMap = null;
//Map of app names for a resource handle id
private Map<Long, String> resourceAppAssociationMap;
private static Logger _logger = LogDomains.getLogger(ConnectionPoolEmitterImpl.class,
LogDomains.RSR_LOGGER);
private List<ConnectorConnPoolAppStatsProvider> ccPoolAppStatsProviders = null;
private ConnectorRuntime runtime;
//keep a static reference to InitialContext so as to avoid performance issues.
private volatile static InitialContext ic = null;
/**
* Constructor
* @param poolInfo connection pool on whose behalf this emitter emits pool related
* probe events
* @param provider
*/
public ConnectionPoolEmitterImpl(PoolInfo poolInfo, ConnectionPoolProbeProvider provider) {
this.poolInfo = poolInfo;
this.poolName = poolInfo.getName();
this.appName = poolInfo.getApplicationName();
this.moduleName = poolInfo.getModuleName();
this.poolProbeProvider = provider;
this.ccPoolAppStatsProviders = new ArrayList<ConnectorConnPoolAppStatsProvider>();
this.appStatsMap = new HashMap<PoolInfo, Map<String, ConnectionPoolAppEmitterImpl>>();
this.resourceAppAssociationMap = new ConcurrentHashMap<Long, String>();
runtime = ConnectorRuntime.getRuntime();
if (ic == null) {
synchronized (ConnectionPoolEmitterImpl.class) {
if(ic == null) {
try{
ic = new InitialContext();
} catch (NamingException e) {
//ignore
}
}
}
}
}
/**
* Fires probe event that a stack trace is to be printed on the server.log.
* The stack trace is mainly related to connection leak tracing for the
* given jdbc connection pool.
* @param stackTrace
*/
public void toString(StringBuffer stackTrace) {
stackTrace.append("\n Monitoring Statistics for \n" + poolName);
poolProbeProvider.toString(poolName, appName, moduleName, stackTrace);
}
/**
* Fires probe event that a connection has been acquired by the application
* for the given jdbc connection pool.
*/
public void connectionAcquired(long resourceHandleId) {
ConnectionPoolAppEmitterImpl appEmitter =
detectAppBasedProviders(getAppName(resourceHandleId));
poolProbeProvider.connectionAcquiredEvent(poolName, appName, moduleName);
if(appEmitter != null) {
appEmitter.connectionAcquired();
}
}
/**
* Fires probe event related to the fact that a connection request is served
* in the time <code>timeTakenInMillis</code> for the given jdbc connection
* pool.
*
* @param timeTakenInMillis time taken to serve a connection
*/
public void connectionRequestServed(long timeTakenInMillis) {
poolProbeProvider.connectionRequestServedEvent(poolName, appName, moduleName, timeTakenInMillis);
}
/**
* Fires probe event related to the fact that the given jdbc connection pool
* has got a connection timed-out event.
*/
public void connectionTimedOut() {
poolProbeProvider.connectionTimedOutEvent(poolName, appName, moduleName);
}
/**
* Fires probe event that a connection under test does not match the
* current request for the given jdbc connection pool.
*/
public void connectionNotMatched() {
poolProbeProvider.connectionNotMatchedEvent(poolName, appName, moduleName);
}
/**
* Fires probe event that a connection under test matches the current
* request for the given jdbc connection pool.
*/
public void connectionMatched() {
poolProbeProvider.connectionMatchedEvent(poolName, appName, moduleName);
}
/**
* Fires probe event that a connection is destroyed for the
* given jdbc connection pool.
*/
public void connectionDestroyed(long resourceHandleId) {
poolProbeProvider.connectionDestroyedEvent(poolName, appName, moduleName);
// Clearing the resource handle id appName mappings stored
// This is useful in cases where connection-leak-reclaim is ON where we destroy
// the connection. In this case, connection-release would not have happened.
resourceAppAssociationMap.remove(resourceHandleId);
}
/**
* Fires probe event that a connection is released for the given jdbc
* connection pool.
*/
public void connectionReleased(long resourceHandleId) {
ConnectionPoolAppEmitterImpl appEmitter =
detectAppBasedProviders(getAppName(resourceHandleId));
poolProbeProvider.connectionReleasedEvent(poolName, appName, moduleName);
if(appEmitter != null) {
appEmitter.connectionReleased();
}
// Clearing the resource handle id appName mappings stored
resourceAppAssociationMap.remove(resourceHandleId);
}
/**
* Fires probe event that a connection is created for the given jdbc
* connection pool.
*/
public void connectionCreated() {
poolProbeProvider.connectionCreatedEvent(poolName, appName, moduleName);
}
/**
* Fires probe event related to the fact that the given jdbc connection pool
* has got a connection leak event.
*
*/
public void foundPotentialConnectionLeak() {
poolProbeProvider.potentialConnLeakEvent(poolName, appName, moduleName);
}
/**
* Fires probe event related to the fact the given jdbc connection pool has
* got a connection validation failed event.
*
* @param count number of times the validation failed
*/
public void connectionValidationFailed(int count) {
poolProbeProvider.connectionValidationFailedEvent(poolName, appName, moduleName, count);
}
/**
* Fires probe event related to the fact the given jdbc connection pool has
* got a connection used event.
*/
public void connectionUsed(long resourceHandleId) {
ConnectionPoolAppEmitterImpl appEmitter =
detectAppBasedProviders(getAppName(resourceHandleId));
poolProbeProvider.connectionUsedEvent(poolName, appName, moduleName);
if (appEmitter != null) {
appEmitter.connectionUsed();
}
}
/**
* Fires probe event related to the fact the given jdbc connection pool has
* got a connection freed event.
*
* @param count number of connections freed to pool
*/
public void connectionsFreed(int count) {
poolProbeProvider.connectionsFreedEvent(poolName, appName, moduleName, count);
}
/**
* Fires probe event related to the fact the given jdbc connection pool has
* got a decrement connection used event.
*
*/
public void decrementConnectionUsed(long resourceHandleId) {
ConnectionPoolAppEmitterImpl appEmitter =
detectAppBasedProviders(getAppName(resourceHandleId));
poolProbeProvider.decrementConnectionUsedEvent(poolName, appName, moduleName);
if(appEmitter != null) {
appEmitter.decrementConnectionUsed();
}
}
/**
* Fires probe event related to the fact the given jdbc connection pool has
* got a decrement free connections size event.
*
*/
public void decrementNumConnFree() {
poolProbeProvider.decrementNumConnFreeEvent(poolName, appName, moduleName);
}
/**
* Fires probe event related to the fact the given jdbc connection pool has
* got a decrement free connections size event.
*
* @param beingDestroyed if the connection is destroyed due to error
* @param steadyPoolSize
*/
public void incrementNumConnFree(boolean beingDestroyed, int steadyPoolSize) {
poolProbeProvider.incrementNumConnFreeEvent(poolName, appName, moduleName, beingDestroyed, steadyPoolSize);
}
/**
* Fires probe event related to the fact the given jdbc connection pool's
* wait queue length has been incremented
*
*/
public void connectionRequestQueued() {
poolProbeProvider.connectionRequestQueuedEvent(poolName, appName, moduleName);
}
/**
* Fires probe event related to the fact the given jdbc connection pool's
* wait queue length has been decremented.
*
*/
public void connectionRequestDequeued() {
poolProbeProvider.connectionRequestDequeuedEvent(poolName, appName, moduleName);
}
private String getAppName(long resourceHandleId) {
// if monitoring is disabled, avoid sending events
// as we need to do "java:app/AppName" to get applicationName for each
// acquire/return connection call which is a performance bottleneck.
if(!runtime.isJdbcPoolMonitoringEnabled() && !runtime.isConnectorPoolMonitoringEnabled()){
return null;
}
String appName = resourceAppAssociationMap.get(resourceHandleId);
if(appName == null){
try {
if(ic == null){
synchronized(ConnectionPoolEmitterImpl.class) {
if(ic == null) {
ic = new InitialContext();
}
}
}
appName = (String) ic.lookup("java:app/AppName");
resourceAppAssociationMap.put(resourceHandleId, appName);
} catch (NamingException ex) {
if (_logger.isLoggable(Level.FINE)) {
_logger.log(Level.FINE, "Unable to get application name using "
+ "java:app/AppName method");
}
}
}
return appName;
}
/**
* Detect if a Stats Provider has already been registered to the
* monitoring framework for this appName and if so, return the specific
* emitter. If not already registered, create and register the
* Stats Provider object to the monitoring framework and add to the list
* of emitters.
*
* @param appName
* @return
*/
private ConnectionPoolAppEmitterImpl detectAppBasedProviders(String appName) {
ConnectionPoolAppProbeProvider probeAppProvider = null;
ConnectionPoolAppEmitterImpl connPoolAppEmitter = null;
if (appName == null) {
//Case when appname cannot be detected. Emitter cannot exist for
//a null appName for any pool.
return null;
}
if (appStatsMap.containsKey(poolInfo)) {
//Some apps have been registered for this pool.
//Find if this appName is already registered.
//All appEmitters for this pool
Map<String, ConnectionPoolAppEmitterImpl> appEmitters = appStatsMap.get(poolInfo);
//Check if the appEmitters list has an emitter for the appName.
ConnectionPoolAppEmitterImpl emitter = appEmitters.get(appName);
if(emitter != null) {
//This appName has already been registered to StatsProviderManager
return emitter;
} else {
if (!ConnectorsUtil.isApplicationScopedResource(poolInfo)) {
//register to the StatsProviderManager and add to the list.
probeAppProvider = registerConnectionPool(appName);
connPoolAppEmitter = addToList(appName, probeAppProvider,
appEmitters);
}
}
} else {
if (!ConnectorsUtil.isApplicationScopedResource(poolInfo)) {
//Does not contain any app providers associated with this poolname
//Create a map of app emitters for the appName and add them to the
//appStatsMap
probeAppProvider = registerConnectionPool(appName);
Map<String, ConnectionPoolAppEmitterImpl> appEmitters =
new HashMap<String, ConnectionPoolAppEmitterImpl>();
connPoolAppEmitter = addToList(appName, probeAppProvider, appEmitters);
}
}
return connPoolAppEmitter;
}
/**
* Register the jdbc/connector connection pool Stats Provider object to the
* monitoring framework under the specific application name monitoring
* sub tree.
*
* @param appName
* @return
*/
private ConnectionPoolAppProbeProvider registerConnectionPool(String appName) {
ResourcePool pool = runtime.getConnectionPoolConfig(poolInfo);
ConnectionPoolAppProbeProvider probeAppProvider =
runtime.getProbeProviderUtil().getConnPoolBootstrap().registerPool(poolInfo, appName);
if (pool instanceof ConnectorConnectionPool) {
probeAppProvider = new ConnectorConnPoolAppProbeProvider();
ConnectorConnPoolAppStatsProvider ccPoolAppStatsProvider =
new ConnectorConnPoolAppStatsProvider(poolInfo, appName);
StatsProviderManager.register(
"connector-connection-pool",
PluginPoint.SERVER,
"resources/" + ConnectorsUtil.escapeResourceNameForMonitoring(poolName) + "/" + appName,
ccPoolAppStatsProvider);
ccPoolAppStatsProviders.add(ccPoolAppStatsProvider);
}
return probeAppProvider;
}
/**
* Add to the pool emitters list. the connection pool application emitter
* for the specific poolInfo and appName.
* @param appName
* @param probeAppProvider
* @param appEmitters
* @return
*/
private ConnectionPoolAppEmitterImpl addToList(String appName,
ConnectionPoolAppProbeProvider probeAppProvider,
Map<String, ConnectionPoolAppEmitterImpl> appEmitters) {
ConnectionPoolAppEmitterImpl connPoolAppEmitter = null;
if (probeAppProvider != null) {
//Add the newly created probe provider to the list.
connPoolAppEmitter = new ConnectionPoolAppEmitterImpl(poolName,
appName, probeAppProvider);
//NOTE : this appName here is different from "appName" instance variable.
appEmitters.put(appName, connPoolAppEmitter);
appStatsMap.put(poolInfo, appEmitters);
}
runtime.getProbeProviderUtil().
getConnPoolBootstrap().addToPoolEmitters(poolInfo, this);
return connPoolAppEmitter;
}
/**
* Unregister the AppStatsProviders registered for this connection pool.
*/
public void unregisterAppStatsProviders() {
runtime.getProbeProviderUtil().getConnPoolBootstrap().unRegisterPool();
Iterator ccProviders = ccPoolAppStatsProviders.iterator();
while (ccProviders.hasNext()) {
ConnectorConnPoolAppStatsProvider ccPoolAppStatsProvider =
(ConnectorConnPoolAppStatsProvider) ccProviders.next();
StatsProviderManager.unregister(ccPoolAppStatsProvider);
}
ccPoolAppStatsProviders.clear();
}
}