patterntesting.runtime.monitor.db.ConnectionMonitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of patterntesting-rt Show documentation
Show all versions of patterntesting-rt Show documentation
PatternTesting Runtime (patterntesting-rt) is the runtime component for
the PatternTesting framework. It provides the annotations and base classes
for the PatternTesting testing framework (e.g. patterntesting-check,
patterntesting-concurrent or patterntesting-exception) but can be also
used standalone for classpath monitoring or profiling.
It uses AOP and AspectJ to perform this feat.
/*
* $Id: ConnectionMonitor.java,v 1.5 2014/05/03 20:02:39 oboehm Exp $
*
* Copyright (c) 2012 by Oliver Boehm
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express orimplied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* (c)reated 07.10.2012 by oliver ([email protected])
*/
package patterntesting.runtime.monitor.db;
import java.sql.Connection;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.management.JMException;
import javax.management.openmbean.*;
import org.slf4j.*;
import patterntesting.runtime.jmx.MBeanHelper;
/**
* This is the monitor class for the {@link ProxyConnection} which monitors the
* different newInstance() and close() call. So you can ask this class how many
* and which connections are open.
*
* The instance of this class is automatically registered as MBean as soon as a
* {@link ProxyConnection} is used.
*
*
* Note: Since 1.4.2 this class was moved from package
* "patterntesting.runtime.db" to here.
*
*
* @author oliver ([email protected])
* @since 1.3 (07.10.2012)
* @version $Revision: 1.5 $
*/
public class ConnectionMonitor implements ConnectionMonitorMBean {
private static final Logger log = LoggerFactory.getLogger(ConnectionMonitor.class);
private static final ConnectionMonitorMBean instance;
private static final List openConnections = new CopyOnWriteArrayList();
private static int sumOfConnections = 0;
static {
instance = new ConnectionMonitor();
log.debug("{} created and registered as MBean.", instance);
try {
MBeanHelper.registerMBean(instance);
} catch (JMException e) {
log.info("{} can't be registered as MBean ({})", instance, e);
}
}
/**
* No need to instantiate it - we provide only some services. The
* constructor is protected because it is still used by the (deprecated)
* ConnectionMonitor in patterntesting.runtime.db.
*/
protected ConnectionMonitor() {
log.trace("{} created.", this);
}
/**
* Yes, it is a Singleton because it offers only some services. So we don't
* need the object twice.
*
* @return the only instance
*/
public static ConnectionMonitorMBean getInstance() {
return instance;
}
/**
* If you want to monitor the connection use this method.
*
* @param connection the original connection
* @return a proxy for the connection
*/
public static Connection getMonitoredConnection(final Connection connection) {
return ProxyConnection.newInstance(connection);
}
/**
* Adds the connection.
*
* @param proxyConnection the proxy connection
*/
public static void addConnection(final ProxyConnection proxyConnection) {
openConnections.add(proxyConnection);
sumOfConnections++;
}
/**
* Removes the connection.
*
* @param proxyConnection the proxy connection
*/
public static void removeConnection(final ProxyConnection proxyConnection) {
openConnections.remove(proxyConnection);
}
/**
* Gets the caller of the given connection.
*
* @param connection the connection
* @return the caller of
*/
public static StackTraceElement getCallerOf(final Connection connection) {
for (ProxyConnection proxy : openConnections) {
if (proxy.getConnection().equals(connection)) {
return proxy.getCaller()[0];
}
}
throw new IllegalArgumentException("not monitored or closed: " + connection);
}
/**
* Gets the callers.
*
* @return the callers
* @see ConnectionMonitorMBean#getCallers()
*/
public StackTraceElement[] getCallers() {
StackTraceElement[] callers = new StackTraceElement[openConnections.size()];
int i = 0;
for (ProxyConnection proxy : openConnections) {
callers[i] = proxy.getCaller()[0];
i++;
}
return callers;
}
/**
* Gets the stacktrace of the caller which opens the last connection.
*
* @return the all caller
*/
public StackTraceElement[] getLastCallerStacktrace() {
ProxyConnection lastConnection = openConnections.get(openConnections.size() - 1);
return lastConnection.getCaller();
}
/**
* Gets the caller stacktraces of all connections.
*
* @return stacktraces of all callers
* @throws OpenDataException the open data exception
*/
public TabularData getCallerStacktraces() throws OpenDataException {
String[] itemNames = { "Caller", "Stacktrace" };
String[] itemDescriptions = { "caller name", "stacktrace" };
try {
OpenType>[] itemTypes = { SimpleType.STRING,
new ArrayType(1, SimpleType.STRING) };
CompositeType rowType = new CompositeType("propertyType",
"property entry", itemNames, itemDescriptions, itemTypes);
TabularDataSupport data = MBeanHelper.createTabularDataSupport(rowType, itemNames);
for (ProxyConnection proxy : openConnections) {
StackTraceElement[] stacktrace = proxy.getCaller();
Map map = new HashMap();
map.put("Caller", stacktrace[0].toString());
map.put("Stacktrace", toStringArray(stacktrace));
CompositeDataSupport compData = new CompositeDataSupport(
rowType, map);
data.put(compData);
}
return data;
} catch (OpenDataException ex) {
log.warn("Cannot get caller stacktraces of " + openConnections.size()
+ " open connections.", ex);
throw ex;
}
}
private static String[] toStringArray(final StackTraceElement[] stacktrace) {
String[] array = new String[stacktrace.length];
for (int i = 0; i < stacktrace.length; i++) {
array[i] = stacktrace[i].toString();
}
return array;
}
/**
* Gets the caller which opens the last connection.
*
* @return the all caller
*/
public StackTraceElement getLastCaller() {
StackTraceElement[] callers = this.getCallers();
if (callers.length == 0) {
log.debug("No open connections - last caller is null.");
return null;
}
return callers[callers.length-1];
}
/**
* Gets the number of open connections.
*
* @return the open count
* @see ConnectionMonitorMBean#getOpenConnections()
*/
public int getOpenConnections() {
return ConnectionMonitor.openConnections.size();
}
/**
* Gets the closed connections.
*
* @return the closed connections
* @since 1.4.1
*/
public int getClosedConnections() {
return this.getSumOfConnections() - this.getOpenConnections();
}
/**
* Gets the total sum of open and closed connections.
*
* @return the sum of connections
* @since 1.4.1
*/
public int getSumOfConnections() {
return sumOfConnections;
}
/**
* Assert that all connections are closed.
*/
public static void assertConnectionsClosed() {
int count = instance.getOpenConnections();
if (count > 0) {
AssertionError error = new AssertionError(count + " connection(s) not closed");
error.setStackTrace(openConnections.iterator().next().getCaller());
throw error;
}
}
/**
* To string.
*
* @return the string
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
int n = this.getOpenConnections();
if (n < 1) {
return this.getClass().getSimpleName();
}
return this.getClass().getSimpleName() + " watching " + n + " connection(s)";
}
}