blob: 62cb034cd2c82ed3e45b03cd0e86b3152ba4c808 [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 org.glassfish.admin.amxtest;
import com.sun.appserv.management.base.AMXDebugSupportMBean;
import com.sun.appserv.management.base.NotificationServiceMgr;
import com.sun.appserv.management.base.QueryMgr;
import com.sun.appserv.management.base.XTypes;
import com.sun.appserv.management.client.AppserverConnectionSource;
import com.sun.appserv.management.client.ConnectionSource;
import com.sun.appserv.management.config.AMXConfig;
import com.sun.appserv.management.config.ConfigConfig;
import com.sun.appserv.management.config.DomainConfig;
import com.sun.appserv.management.config.ModuleMonitoringLevelValues;
import com.sun.appserv.management.config.ModuleMonitoringLevelsConfig;
import com.sun.appserv.management.config.NodeAgentConfig;
import com.sun.appserv.management.config.ServerConfig;
import com.sun.appserv.management.config.ServersConfig;
import com.sun.appserv.management.util.jmx.MBeanServerConnectionConnectionSource;
import com.sun.appserv.management.util.misc.GSetUtil;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import org.glassfish.admin.amx.base.BulkAccess;
import org.glassfish.admin.amx.base.DomainRoot;
import org.glassfish.admin.amx.base.SystemInfo;
import org.glassfish.admin.amx.core.proxy.ProxyFactory;
import org.glassfish.admin.amx.j2ee.J2EEDomain;
import org.glassfish.admin.amx.util.ClassUtil;
import org.glassfish.admin.amx.util.ExceptionUtil;
import org.glassfish.admin.amx.util.TypeCast;
import org.glassfish.admin.amx.util.jmx.JMXUtil;
import org.glassfish.external.amx.AMX;
import javassist.bytecode.analysis.Util;
import static org.junit.jupiter.api.Assertions.fail;
/**
Base class for testing the AMX API
*/
public class AMXTestBase
extends JMXTestBase {
private ConnectionSource mConnectionSource;
private final DomainRoot mDomainRoot;
private final ProxyFactory mProxyFactory;
private final Set<ObjectName> mNotTested;
private final TestUtil mTestUtil;
/**
config name for PE (non-EE) configuration
*/
protected static final String PE_CONFIG_NAME = "server-config";
private final static boolean WEB_MODULE_MONITOR_BROKEN = true;
private static boolean MONITORING_ENABLED = false;
protected static final String NEWLINE = System.getProperty("line.separator");
public AMXTestBase() {
checkConnection(getConnection());
mNotTested = new HashSet<ObjectName>();
try {
mConnectionSource =
new MBeanServerConnectionConnectionSource(getConnection());
mProxyFactory = ProxyFactory.getInstance(mConnectionSource, true);
mDomainRoot = mProxyFactory.getDomainRoot();
}
catch (Exception e) {
throw new RuntimeException(e.toString(), e);
}
mTestUtil = new TestUtil(getDomainRoot());
}
protected Map<String, ServerConfig>
getServerConfigMap( final ServersConfig sc )
{
final Map<String,ServerConfig> all = new HashMap<String,ServerConfig>();
all.putAll( sc.getStandaloneServerConfigMap() );
all.putAll( sc.getClusteredServerConfigMap() );
return all;
}
protected Set<AMX>
getAllAMX() {
return getTestUtil().getAllAMX();
}
protected ModuleMonitoringLevelsConfig
getModuleMonitoringLevelsConfig(final String configName) {
final ConfigConfig cc = configName == null ? getConfigConfig() : getConfigConfig(configName);
assert (cc != null);
final ModuleMonitoringLevelsConfig mon =
cc.getMonitoringServiceConfig().getModuleMonitoringLevelsConfig();
return (mon);
}
protected ModuleMonitoringLevelsConfig
getModuleMonitoringLevelsConfig() {
return getModuleMonitoringLevelsConfig(null);
}
private void changeAllLevels( final ModuleMonitoringLevelsConfig config, final String value )
{
final Set<String> names = ModuleMonitoringLevelsConfig.ALL_LEVEL_NAMES;
final AttributeList attrs = new AttributeList();
for( final String name : names )
{
attrs.add( new Attribute( name, value ) );
}
try {
Util.getExtra(config).setAttributes( attrs );
} catch ( Exception e ) {
// ignore
}
}
/**
Ensure that monitoring is enabled so that unit tests don't miss anything
*/
protected synchronized void
setMonitoring( final String value ) {
synchronized (AMXTestBase.class) {
final String[] configNames = getConfigNames();
for (int i = 0; i < configNames.length; ++i) {
final ModuleMonitoringLevelsConfig mml = getModuleMonitoringLevelsConfig(configNames[i] );
changeAllLevels( mml, value );
}
}
}
protected void
turnOffMonitoring() {
setMonitoring( ModuleMonitoringLevelValues.OFF );
MONITORING_ENABLED = false;
}
protected void
turnOnMonitoring() {
setMonitoring( ModuleMonitoringLevelValues.HIGH );
MONITORING_ENABLED = true;
}
private static final Set<String> EXPECTED_REMOTE_INCOMPLETE_TYPES =
GSetUtil.newUnmodifiableStringSet(XTypes.CALL_FLOW_MONITOR, XTypes.LOGGING);
protected boolean
isRemoteIncomplete(final ObjectName objectName) {
final AMX amx = getProxyFactory().getProxy(objectName, AMX.class);
final String j2eeType = amx.getJ2EEType();
final boolean isRemote = !amx.isDAS();
if (isRemote &&
!EXPECTED_REMOTE_INCOMPLETE_TYPES.contains(j2eeType)) {
warning("isRemoteIncomplete: not expecting j2eeType=" + j2eeType +
", has the implementation changed?");
}
return isRemote;
}
protected boolean
shouldTest(final ObjectName objectName) {
boolean shouldTest = !isRemoteIncomplete(objectName);
return (shouldTest);
}
protected void
waitUnregistered(final ObjectName objectName)
throws IOException {
final MBeanServerConnection conn = getConnection();
if (conn.isRegistered(objectName)) {
mySleep(100);
}
while (conn.isRegistered(objectName)) {
trace("waitUnregistered: " + objectName);
mySleep(100);
}
}
/**
Because proxies are cleaned up asynchronously, it's possible for one
to remain in the factory until the factory processes the Notification
that it's MBean has been unregistered.
*/
protected void
waitProxyGone(
final ProxyFactory factory,
final ObjectName objectName) {
long millis = 1;
while (factory.getProxy(objectName, AMX.class, false) != null) {
mySleep(millis);
millis *= 2;
trace("waitProxyGone: waiting for proxy to disappear: " + objectName);
}
}
protected final void
notTested(final ObjectName objectName) {
if (isRemoteIncomplete(objectName)) {
trace("remoteIncomplete (this is OK): " + objectName);
} else {
mNotTested.add(objectName);
}
}
protected final Set<ObjectName>
getNotTestedSet() {
return (mNotTested);
}
public static void
mySleep(final long millis) {
try {
Thread.sleep(millis);
}
catch (InterruptedException e) {
}
}
protected final void
warnNotTested() {
final Set<ObjectName> notTested = getNotTestedSet();
if (notTested.size() != 0) {
final Set j2eeTypes =
JMXUtil.getKeyPropertySet(AMX.J2EE_TYPE_KEY, notTested);
trace("WARNING: DID NOT TEST: " + notTested.size() + " MBeans of types {" +
toString(j2eeTypes) + "}");
}
}
protected void
checkConnection(final MBeanServerConnection conn) {
assert (getConnection() != null);
try {
conn.isRegistered(JMXUtil.getMBeanServerDelegateObjectName());
}
catch (Exception e) {
fail("Connection failed:\n" +
ExceptionUtil.getStackTrace(getRootCause(e)));
}
}
protected void
checkConnection() {
checkConnection(getConnection());
}
protected TestUtil
getTestUtil() {
return mTestUtil;
}
protected final AMX
getProxy(final ObjectName objectName) {
final ProxyFactory factory = ProxyFactory.getInstance(getConnectionSource(), true);
final AMX proxy = factory.getProxy(objectName, AMX.class);
return (proxy);
}
/**
We don't have T extend AMX because not all mixin interfaces extend AMX.
*/
protected final <T> T
getProxy(
final ObjectName objectName,
final Class<T> theClass) {
return (theClass.cast(getProxy(objectName)));
}
protected final DomainRoot
getDomainRoot() {
assert (mDomainRoot != null) : "mDomainRoot is null";
return (mDomainRoot);
}
protected final DomainConfig
getDomainConfig() {
return (getDomainRoot().getDomainConfig());
}
protected final J2EEDomain
getJ2EEDomain() {
return (getDomainRoot().getJ2EEDomain());
}
protected String[]
getConfigNames() {
final Map<String, ConfigConfig> configMap =
getDomainConfig().getConfigsConfig().getConfigConfigMap();
return (GSetUtil.toStringArray(configMap.keySet()));
}
protected ConfigConfig
getConfigConfig(final String name) {
final Map<String, ConfigConfig> configs = getDomainConfig().getConfigsConfig().getConfigConfigMap();
return configs.get(name == null ? PE_CONFIG_NAME : name);
}
protected static ConfigConfig
getConfigConfig(final AMXConfig any) {
final ObjectName objectName = Util.getObjectName(any);
final String configName = objectName.getKeyProperty(XTypes.CONFIG_CONFIG);
return any.getDomainRoot().getDomainConfig().getConfigsConfig().getConfigConfigMap().get(configName);
}
protected ConfigConfig
getConfigConfig() {
return (getConfigConfig(PE_CONFIG_NAME));
}
protected QueryMgr
getQueryMgr() {
assert (mDomainRoot != null);
final QueryMgr proxy = getDomainRoot().getQueryMgr();
assert (proxy != null);
return (proxy);
}
protected NotificationServiceMgr
getNotificationServiceMgr() {
return (getDomainRoot().getNotificationServiceMgr());
}
protected BulkAccess
getBulkAccess() {
return (getDomainRoot().getBulkAccess());
}
protected ConnectionSource
getConnectionSource() {
assert (mConnectionSource != null);
return (mConnectionSource);
}
protected MBeanServerConnection
getConnection() {
return (getGlobalMBeanServerConnection());
}
protected ProxyFactory
getProxyFactory() {
return (mProxyFactory);
}
protected Class
getInterfaceClass(AMX proxy)
throws ClassNotFoundException {
final String name = Util.getExtra(proxy).getInterfaceName();
return (ClassUtil.getClassFromName(name));
}
protected <T extends AMX> boolean
testOnProxies(
final Collection<T> proxies,
final Method method)
throws Exception {
final long start = now();
boolean failed = false;
int testCount = 0;
final Object[] args = new Object[1];
for (final T proxy : proxies) {
final ObjectName objectName = Util.getExtra(proxy).getObjectName();
if (!shouldTest(objectName)) {
notTested(objectName);
continue;
}
++testCount;
try {
args[0] = proxy;
method.invoke(this, args);
}
catch (Exception e) {
trace(method.getName() + " failed for proxy: " +
quote(JMXUtil.toString(objectName)));
failed = true;
trace(ExceptionUtil.toString(e));
}
}
final long elapsed = now() - start;
printVerbose("Ran test method " + method.getName() + " on " + testCount +
" candidates in " + elapsed + "ms");
warnNotTested();
warnNotTested();
return (!failed);
}
protected boolean
testOnObjectNames(
final Collection<ObjectName> objectNames,
final Method method)
throws Exception {
boolean failed = false;
final Object[] args = new Object[1];
int testCount = 0;
final long start = now();
for (final ObjectName objectName : objectNames) {
if (!shouldTest(objectName)) {
notTested(objectName);
continue;
}
++testCount;
try {
args[0] = objectName;
method.invoke(this, args);
}
catch (Exception e) {
final Throwable rootCause = getRootCause(e);
trace(method.getName() + " failed for: " +
quote(JMXUtil.toString(objectName)) + " with Exception of type " +
rootCause.getClass().getName() + ", msg = " + rootCause.getMessage());
failed = true;
}
}
final long elapsed = now() - start;
printVerbose("Ran test method " + method.getName() + " on " + testCount +
" candidates in " + elapsed + "ms");
warnNotTested();
return (!failed);
}
protected final static Class[] OBJECTNAME_SIG = new Class[]{ObjectName.class};
protected final static Class[] PROXY_SIG = new Class[]{AMX.class};
protected void
testAll(
final Collection<ObjectName> objectNames,
final String methodName)
throws Exception {
final boolean success = testOnObjectNames(objectNames,
this.getClass().getMethod(methodName, OBJECTNAME_SIG));
assert (success);
}
protected <T extends AMX> void
testAllProxies(
final Collection<T> proxies,
final String methodName)
throws Exception {
final boolean success = testOnProxies(proxies,
this.getClass().getMethod(methodName, PROXY_SIG));
assert (success);
}
protected void
testAll(String methodName)
throws Exception {
final Set<ObjectName> names = getTestUtil().getAllObjectNames();
testAll(names, methodName);
}
public void
setUp()
throws Exception {
super.setUp();
turnOnMonitoring();
}
public void
testAssertsOn() {
checkAssertsOn();
}
private static final String DEFAULT_INSTANCE_NAME = "test";
protected static String
getDefaultInstanceName(final String qualifier) {
String name = null;
if (qualifier == null) {
name = DEFAULT_INSTANCE_NAME;
} else {
name = qualifier + "." + DEFAULT_INSTANCE_NAME;
}
return name;
}
protected Throwable
getRootCause(Throwable t) {
return ExceptionUtil.getRootCause(t);
}
protected String
getStackTrace(Throwable t) {
return ExceptionUtil.getStackTrace(t);
}
protected String
getRootCauseStackTrace(Throwable t) {
return getStackTrace(getRootCause(t));
}
protected Map<String, AppserverConnectionSource>
getNodeAgents() {
final Map<?, ?> m = Map.class.cast(getEnvValue(PropertyKeys.NODE_AGENTS_KEY));
return TypeCast.checkedMap(m,
String.class, AppserverConnectionSource.class);
}
protected String
getDASNodeAgentName() {
return getEnvString(PropertyKeys.DAS_NODE_AGENT_NAME, null);
}
protected NodeAgentConfig
getDASNodeAgentConfig() {
final String name = getDASNodeAgentName();
NodeAgentConfig config = null;
if (name != null) {
config = getDomainConfig().getNodeAgentsConfig().getNodeAgentConfigMap().get(name);
}
return config;
}
protected boolean
getTestOffline() {
return getEnvBoolean(PropertyKeys.TEST_OFFLINE_KEY, false);
}
/**
Check if we're testing in Offline mode, which means that
Config MBeans are loaded in-process. If so, issue a warning.
@return true if test should be run, false if in offline mode
*/
protected boolean
checkNotOffline(final String testName) {
boolean offline = getTestOffline();
if (offline) {
//warning( "amxtest.testOffline=true, skipping test " + testName + "()" );
}
return !offline;
}
public static Capabilities
getDefaultCapabilities() {
return getOfflineCapableCapabilities(true);
}
protected static Capabilities
getOfflineCapableCapabilities(boolean offlineCapable) {
final Capabilities c = new Capabilities();
c.setOfflineCapable(offlineCapable);
return c;
}
AMXDebugSupportMBean
getAMXDebugSupportMBean() {
final ObjectName objectName = Util.newObjectName(AMXDebugSupportMBean.OBJECT_NAME);
try {
return (AMXDebugSupportMBean) newProxy(objectName, AMXDebugSupportMBean.class);
}
catch (Exception e) {
assert false : "Can't get proxy to " + objectName;
}
return null;
}
protected boolean
supportsMultipleServers() {
return supportsMultipleServers(getDomainRoot());
}
public static boolean
supportsMultipleServers(final DomainRoot domainRoot) {
return domainRoot.getSystemInfo().supportsFeature(SystemInfo.MULTIPLE_SERVERS_FEATURE);
}
protected boolean
supportsClusters() {
return getDomainRoot().getSystemInfo().supportsFeature(SystemInfo.CLUSTERS_FEATURE);
}
}