/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 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. */ package org.glassfish.admin.amxtest; import com.sun.appserv.management.DomainRoot; import com.sun.appserv.management.base.AMXDebug; import com.sun.appserv.management.base.SystemInfo; import com.sun.appserv.management.base.Util; import com.sun.appserv.management.client.AppserverConnectionSource; import com.sun.appserv.management.client.ConnectionSource; import com.sun.appserv.management.client.HandshakeCompletedListenerImpl; import com.sun.appserv.management.client.TLSParams; import com.sun.appserv.management.config.JMXConnectorConfig; import com.sun.appserv.management.config.NodeAgentConfig; import com.sun.appserv.management.config.NodeAgentsConfig; import com.sun.appserv.management.config.OfflineConfigIniter; import com.sun.appserv.management.util.jmx.JMXUtil; import com.sun.appserv.management.util.jmx.MBeanServerConnectionSource; import com.sun.appserv.management.util.jmx.stringifier.StringifierRegistryIniter; import com.sun.appserv.management.util.misc.ClassUtil; import com.sun.appserv.management.util.misc.ExceptionUtil; import com.sun.appserv.management.util.misc.FileUtils; import com.sun.appserv.management.util.misc.GSetUtil; import com.sun.appserv.management.util.misc.MapUtil; import com.sun.appserv.management.util.misc.StringUtil; import com.sun.appserv.management.util.misc.TypeCast; import com.sun.appserv.management.util.stringifier.ArrayStringifier; import com.sun.appserv.management.util.stringifier.SmartStringifier; import com.sun.appserv.management.util.stringifier.StringifierRegistryImpl; import com.sun.appserv.management.helper.AttributeResolverHelper; import static org.glassfish.admin.amxtest.PropertyKeys.*; import org.glassfish.admin.amxtest.monitor.AMXMonitorTestBase; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.MBeanServerFactory; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.remote.JMXConnectionNotification; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import junit.framework.TestCase; /** Main class that runs all the unit tests */ public final class TestMain implements NotificationListener { private final DomainRoot mDomainRoot; private HandshakeCompletedListenerImpl mHandshakeCompletedListener; private static void printUsage() { println("USAGE: java " + TestMain.class.getName() + " <properties-file> [name=value [name=value]*]"); final String example = MapUtil.toString(PropertyKeys.getDefaults(), "\n") + "\n\nAdditional properties may be included and will be placed into a Map " + "for use by any unit test."; println("Properties file format:\n" + example); println(""); println("The optional property " + StringUtil.quote(TEST_CLASSES_FILE_KEY) + " may contain the name of a file which specifies which test classes to run. " + "Files should be listed with fully-qualified classnames, one per line. " + "The # character may be used to comment-out classnames." ); println(""); println("Additional properties may also be passed directly on the command line."); println("These override any properties found in the specified properties file."); println("[all properties intended for permanent use should be defined in PropertyKeys.java]"); println("EXAMPLE:"); println("java TestMain amxtest.properties amxtest.verbose=true my-temp=true"); } private static boolean isHelp(final String s) { return (s.equals("help") || s.equals("--help") || s.equals("-?")); } protected static void checkAssertsOn() { try { assert (false); throw new Error("TestMain(): Assertions must be enabled for unit tests!"); } catch (AssertionError a) { } } private static Map<String, String> argsToMap(final String[] args) { final Map<String, String> params = new HashMap<String, String>(); params.put(DEFAULT_PROPERTIES_FILE, args[0]); for (int i = 1; i < args.length; ++i) { final String pair = args[i]; final int delimIndex = pair.indexOf("="); String name = null; String value = null; if (delimIndex < 0) { name = pair; value = null; } else { name = pair.substring(0, delimIndex); value = pair.substring(name.length() + 1, pair.length()); } params.put(name, value); } return params; } private static DomainRoot initOffline(final File domainXML) { final MBeanServer server = MBeanServerFactory.createMBeanServer("test"); assert (domainXML.exists() && domainXML.length() != 0); final OfflineConfigIniter initer = new OfflineConfigIniter(server, domainXML); final DomainRoot domainRoot = initer.getDomainRoot(); return domainRoot; } public static void main(final String[] args) throws Exception { checkAssertsOn(); // for friendlier output via Stringifiers new StringifierRegistryIniter(StringifierRegistryImpl.DEFAULT); if (args.length == 0 || (args.length == 1 && isHelp(args[0]))) { printUsage(); System.exit(255); } final Map<String, String> cmdLineParams = argsToMap(args); try { new TestMain(args.length == 0 ? null : args[0], cmdLineParams); } catch (Throwable t) { final Throwable rootCause = ExceptionUtil.getRootCause(t); if (rootCause instanceof java.net.ConnectException) { System.err.println("\nERROR: The connection to the server could not be made"); } else { System.err.println("\nERROR: exception of type: " + rootCause.getClass().getName()); rootCause.printStackTrace(); } System.exit(-1); } } private static void println(Object o) { System.out.println(o); } public static String toString(Object o) { return (SmartStringifier.toString(o)); } private final DomainRoot getDomainRoot() { return (mDomainRoot); } private TLSParams createTLSParams( final File trustStoreFile, final String password) { final char[] trustStorePassword = password.toCharArray(); mHandshakeCompletedListener = new HandshakeCompletedListenerImpl(); final TestClientTrustStoreTrustManager trustMgr = new TestClientTrustStoreTrustManager(trustStoreFile, trustStorePassword); final TLSParams tlsParams = new TLSParams(trustMgr, mHandshakeCompletedListener); return (tlsParams); } /** Read connect properties from a file. */ private final Map<String, String> getProperties(final String file) throws IOException { Map<String, String> props = PropertyKeys.getDefaults(); props.remove(TEST_CLASSES_FILE_KEY); if (file != null) { println("Reading properties from: " + StringUtil.quote(file)); final String propsString = FileUtils.fileToString(new File(file)); final Properties fromFile = new Properties(); fromFile.load(new ByteArrayInputStream(propsString.getBytes())); props = MapUtil.toStringStringMap(fromFile); } else { println("Using default properties."); } return (props); } /** @param host hostname or IP address of Domain Admin Server @param port RMI administrative port @param user admin user @param password admin user password @param tlsParams TLS parameters, may be null @return AppserverConnectionSource */ public static AppserverConnectionSource connect( final String host, final int port, final String user, final String password, final TLSParams tlsParams) throws IOException { final String info = "host=" + host + ", port=" + port + ", user=" + user + ", password=" + password + ", tls=" + (tlsParams != null); println("Connecting: " + info + "..."); final AppserverConnectionSource conn = new AppserverConnectionSource(AppserverConnectionSource.PROTOCOL_JMXMP, host, port, user, password, tlsParams, null); conn.getJMXConnector(false); //println( "Connected: " + info ); return (conn); } private final class PropertyGetter { final Map<String, Object> mItems; public PropertyGetter(final Map<String, Object> props) { mItems = new HashMap<String, Object>(); mItems.putAll(props); } public Object get(final String key) { Object result = System.getProperty(key); if (result == null) { result = mItems.get(key); } return (result); } public String getString(final String key) { return ((String) get(key)); } public File getFile(final String key) { final String value = getString(key); return (value == null ? null : new File(value)); } public int getint(final String key) { return (Integer.parseInt(getString(key))); } public Integer getInteger(final String key) { return (new Integer(getString(key))); } public boolean getboolean(final String key) { return (Boolean.valueOf(getString(key)).booleanValue()); } public Boolean getBoolean(final String key) { return (Boolean.valueOf(getString(key))); } } ; private AppserverConnectionSource _getConnectionSource( final PropertyGetter getter, final String host, final int port) throws IOException { final String user = getter.getString(USER_KEY); final String password = getter.getString(PASSWORD_KEY); final File trustStore = getter.getFile(TRUSTSTORE_KEY); final String trustStorePassword = getter.getString(TRUSTSTORE_PASSWORD_KEY); final boolean useTLS = getter.getboolean(USE_TLS_KEY); final TLSParams tlsParams = useTLS ? createTLSParams(trustStore, trustStorePassword) : null; AppserverConnectionSource conn = null; try { conn = connect(host, port, user, password, tlsParams); if (mHandshakeCompletedListener != null) { assert (mHandshakeCompletedListener.getLastEvent() != null); println("HandshakeCompletedEvent: " + toString(mHandshakeCompletedListener.getLastEvent())); } } catch (IOException e) { if (useTLS) { // try without TLS println("Attempting connection without TLS..."); conn = connect(host, port, user, password, null); } } if (conn != null) { conn.getJMXConnector(false).addConnectionNotificationListener(this, null, conn); } return (conn); } private AppserverConnectionSource _getConnectionSource(final PropertyGetter getter) throws IOException { final String host = getter.getString(HOST_KEY); final int port = getter.getint(PORT_KEY); return _getConnectionSource(getter, host, port); } private AppserverConnectionSource getConnectionSource( final PropertyGetter getter, boolean retry) throws Exception { AppserverConnectionSource conn = null; final long PAUSE_MILLIS = 3 * 1000; for (int i = 0; i < 5; ++i) { try { conn = _getConnectionSource(getter); break; } catch (Exception e) { final Throwable rootCause = ExceptionUtil.getRootCause(e); if (rootCause instanceof java.net.ConnectException) { println("ConnectException: " + rootCause.getMessage() + "...retry..."); Thread.sleep(PAUSE_MILLIS); continue; } throw e; } } return (conn); } public void handleNotification( final Notification notifIn, final Object handback) { if (notifIn instanceof JMXConnectionNotification) { final String type = notifIn.getType(); if (type.equals(JMXConnectionNotification.FAILED)) { System.err.println("\n\n### JMXConnection FAILED: " + handback + "\n\n"); } else if (type.equals(JMXConnectionNotification.CLOSED)) { System.err.println("\n\n### JMXConnection CLOSED: " + handback + "\n\n"); } else if (type.equals(JMXConnectionNotification.OPENED)) { System.err.println("\n\n### JMXConnection OPENED: " + handback + "\n\n"); } else if (type.equals(JMXConnectionNotification.NOTIFS_LOST)) { System.err.println("\n\n### JMXConnection NOTIFS_LOST: " + handback + "\n\n" + notifIn); Observer.getInstance().notifsLost(); } } } private void printItems( final String[] items, final String prefix) { for (int i = 0; i < items.length; ++i) { println(prefix + items[i]); } } private String[] classesToStrings(final Set<Class<TestCase>> classes) { final String[] names = new String[classes.size()]; int i = 0; for (final Class<?> c : classes) { names[i] = c.getName(); ++i; } return names; } private void warnUntestedClasses(final List<Class<TestCase>> actual) { final Set<Class<TestCase>> actualSet = GSetUtil.newSet(actual); final Set<Class<TestCase>> allSet = GSetUtil.newSet(Tests.getTestClasses()); final Set<Class<TestCase>> untested = GSetUtil.newSet(allSet); untested.removeAll(actualSet); if (untested.size() != 0) { println("\nWARNING: the following tests WILL NOT BE RUN:"); final String[] names = classesToStrings(untested); for (int i = 0; i < names.length; ++i) { names[i] = "!" + names[i] + "!"; // indicate not being run } println(ArrayStringifier.stringify(names, "\n")); println(""); } final Set<Class<TestCase>> extras = GSetUtil.newSet(actualSet); extras.removeAll(actualSet); if (extras.size() != 0) { println("\nNOTE: the following non-default tests WILL BE RUN:"); final String[] names = classesToStrings(extras); println(ArrayStringifier.stringify(names, "\n")); println(""); } } private void warnDisabledTests() { final String WARNING = "----------------------------------------\n" + "- -\n" + "- NOTE: -\n" + "- Generic tests currently disabled for -\n" + "- AMX MBeans which reside in non-DAS -\n" + "- server instances eg Logging, CallFlow.-\n" + "- Denoted by 'remoteIncomplete' -\n" + "- -\n" + "- -\n" + "----------------------------------------"; println(WARNING); } private List<Class<TestCase>> getTestClasses(final File testsFile) throws FileNotFoundException, IOException { List<Class<TestCase>> testClasses = null; if (testsFile == null) { testClasses = Tests.getTestClasses(); println("NO TEST FILE SPECIFIED--TESTING ALL CLASSES in " + Tests.class.getName()); } else { println("Reading test classes from: " + StringUtil.quote(testsFile.toString())); String fileString = null; try { fileString = FileUtils.fileToString(testsFile); } catch( final IOException e ) { println( "Unable to open file " + testsFile.getAbsolutePath() ); throw e; } final String temp = fileString.replaceAll("\r\n", "\n").replaceAll("\r", "\n"); final String[] classnames = temp.split("\n"); testClasses = new ArrayList<Class<TestCase>>(); for (int i = 0; i < classnames.length; ++i) { final String classname = classnames[i].trim(); if (classname.length() != 0 && !classname.startsWith("#")) { try { // println( "Looking for class " + StringUtil.quote(classname) ); final Class<TestCase> theClass = TypeCast.asClass(ClassUtil.getClassFromName(classname)); testClasses.add(theClass); } catch (Throwable t) { final String msg = "Can't load test class " + StringUtil.quote(classname); println( msg ); throw new Error(msg, t); } } } warnUntestedClasses(testClasses); warnDisabledTests(); } return (testClasses); } private void warnUnknownProperties(final Map<String, String> props) { final Map<String, String> known = new HashMap<String, String>(getDefaults()); final Map<String, String> unknown = new HashMap<String, String>(props); unknown.keySet().removeAll(known.keySet()); if (unknown.keySet().size() != 0) { println("\nNOTE: the following properties are not recognized but " + "will be included in the environment for use by unit tests:"); println(MapUtil.toString(unknown, "\n")); println(""); } } private static final String RMI_PROTOCOL_IN_CONFIG = "rmi_jrmp"; public Map<String, AppserverConnectionSource> getNodeAgentConnections( final DomainRoot domainRoot, final PropertyGetter getter) { final NodeAgentsConfig nacs = domainRoot.getDomainConfig().getNodeAgentsConfig(); if ( nacs == null ) return null; final Map<String, NodeAgentConfig> nodeAgentConfigs = nacs.getNodeAgentConfigMap(); final Map<String, AppserverConnectionSource> nodeAgentConnections = new HashMap<String, AppserverConnectionSource>(); println(""); println("Contacting node agents..."); for (final NodeAgentConfig nodeAgentConfig : nodeAgentConfigs.values()) { final String nodeAgentName = nodeAgentConfig.getName(); final JMXConnectorConfig connConfig = nodeAgentConfig.getJMXConnectorConfig(); final AttributeResolverHelper r = new AttributeResolverHelper(connConfig); if (! r.resolveBoolean("Enabled") ) { println(nodeAgentName + ": DISABLED CONNECTOR"); continue; } final String address = connConfig.getAddress(); final int port = r.resolveInt("Port"); final boolean tlsEnabled = r.resolveBoolean( "SecurityEnabled" ); final String protocol = connConfig.getProtocol(); if (!RMI_PROTOCOL_IN_CONFIG.equals(protocol)) { println(nodeAgentName + ": UNSUPPORTED CONNECTOR PROTOCOL: " + protocol); continue; } // See if we can connect try { final AppserverConnectionSource asConn = _getConnectionSource(getter, address, port); final MBeanServerConnection conn = asConn.getMBeanServerConnection(false); final boolean alive = conn.isRegistered(JMXUtil.getMBeanServerDelegateObjectName()); assert (alive); nodeAgentConnections.put(nodeAgentName, asConn); println(nodeAgentName + ": ALIVE"); } catch (Exception e) { println("Node agent " + nodeAgentConfig.getName() + " could not be contacted: " + e.getClass().getName()); println(nodeAgentName + ": COULD NOT BE CONTACTED"); continue; } } println(""); return nodeAgentConnections; } private Capabilities getCapabilities(final Class c) { Capabilities capabilities = AMXTestBase.getDefaultCapabilities(); try { final Method getCapabilities = c.getDeclaredMethod("getCapabilities", (Class[]) null); capabilities = (Capabilities) getCapabilities.invoke(null, (Object[]) null); } catch (Exception e) { } return capabilities; } private List<Class<TestCase>> filterTestClasses( final DomainRoot domainRoot, final PropertyGetter getter, final List<Class<TestCase>> classes) { final boolean offline = getter.getboolean(TEST_OFFLINE_KEY); final SystemInfo systemInfo = domainRoot == null ? null : domainRoot.getSystemInfo(); final boolean clustersSupported = systemInfo == null ? false : systemInfo.supportsFeature(SystemInfo.CLUSTERS_FEATURE); final boolean multipleServersSupported = systemInfo == null ? false : systemInfo.supportsFeature(SystemInfo.MULTIPLE_SERVERS_FEATURE); final boolean monitorsSupported = !offline; final List<Class<TestCase>> included = new ArrayList<Class<TestCase>>(); final List<Class<TestCase>> omitted = new ArrayList<Class<TestCase>>(); for (final Class<TestCase> c : classes) { boolean include = true; Capabilities capabilities = null; try { capabilities = getCapabilities(c); } catch( Throwable t ) { println( "WARNING: cannot getCapabilities() from " + c.getClass().getName() + ": " + t ); continue; } if ((!monitorsSupported) && AMXMonitorTestBase.class.isAssignableFrom(c)) { include = false; } else if (offline && !capabilities.getOfflineCapable()) { include = false; } else if (ClusterSupportRequired.class.isAssignableFrom(c) && !clustersSupported) { include = false; } else if (MultipleServerSupportRequired.class.isAssignableFrom(c) && !multipleServersSupported) { include = false; } if (include) { included.add(c); } else { omitted.add(c); } } return included; } private File mDefaultDir; static File getDefaultDir( final String propsFile ) { File dir = null; if ( propsFile != null ) { final File pf = new File(propsFile).getAbsoluteFile(); dir = pf.getParentFile().getAbsoluteFile(); } else { dir = new File(System.getProperty("user.dir")); } return dir; } /** */ public TestMain( final String optionalPropertiesFile, final Map<String, String> cmdLineParams) throws Exception { AMXDebug.getInstance().setAll(true); checkAssertsOn(); mDefaultDir = getDefaultDir(optionalPropertiesFile); final Map<String, String> props = getProperties(optionalPropertiesFile); final Map<String, String> envIn = new HashMap<String, String>(props); envIn.putAll(cmdLineParams); warnUnknownProperties(envIn); final Map<String, Object> env = new HashMap<String, Object>(); env.putAll(envIn); println(""); println("ENVIRONMENT:\n" + MapUtil.toString(env, "\n")); println(""); final PropertyGetter getter = new PropertyGetter(env); ConnectionSource conn = null; final boolean testOffline = getter.getboolean(TEST_OFFLINE_KEY); if (testOffline) { final String domainXML = getter.getString(DOMAIN_XML_KEY); mDomainRoot = initOffline(new File(domainXML)); final MBeanServer server = (MBeanServer) Util.getExtra(mDomainRoot).getConnectionSource().getExistingMBeanServerConnection(); final Set<ObjectName> mbeans = JMXUtil.queryNames(server, Util.newObjectName("*:*"), null); //println( "\n\n------------------------------------------" ); //println( "MBeans registered:" ); //println( CollectionUtil.toString( mbeans, "\n" ) ); //println( "\n\n" ); conn = new MBeanServerConnectionSource(server); } else { if (getter.getboolean(CONNECT_KEY)) { final AppserverConnectionSource acs = getConnectionSource(getter, true); if (acs == null) { throw new IOException("Can't connect to server"); } mDomainRoot = acs.getDomainRoot(); conn = acs; } else { mDomainRoot = null; conn = null; } } if (mDomainRoot != null) { Observer.create(mDomainRoot); } final boolean expandedTesting = testOffline ? false : getter.getboolean(EXPANDED_TESTING_KEY); if (mDomainRoot != null && expandedTesting) { final Map<String, AppserverConnectionSource> connections = getNodeAgentConnections(mDomainRoot, getter); env.put(NODE_AGENTS_KEY, connections); } final boolean threaded = getter.getboolean(RUN_THREADED_KEY); if (getter.getboolean(VERBOSE_KEY)) { println("VERBOSE mode enabled"); if (threaded) { println("NOTE: timings displayed when running " + "threaded tests will be impacted by other concurrent tests."); } } final File temp = new File(TEST_CLASSES_FILE_KEY); final File classesFile = temp.isAbsolute() ? temp : new File( mDefaultDir, getter.getString(TEST_CLASSES_FILE_KEY)); println( "Default directory: " + mDefaultDir ); println( "Classes file: " + classesFile ); final List<Class<TestCase>> specifiedClasses = getTestClasses(classesFile); final List<Class<TestCase>> testClasses = filterTestClasses(mDomainRoot, getter, specifiedClasses); final int iterations = getter.getInteger(ITERATIONS_KEY).intValue(); iterateTests( testClasses, iterations, conn, threaded, Collections.unmodifiableMap(env)); println(""); println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"); println(">>>> Please inspect amxtest.coverage <<<<"); println(" ^ "); println(" ^ "); println(" ^ "); println(" ^ "); } private void iterateTests( final List<Class<TestCase>> testClasses, final int iterations, final ConnectionSource conn, final boolean threaded, final Map<String, Object> env) throws Exception { for (int i = 0; i < iterations; ++i) { if (iterations != 1) { println("#########################################################"); println("\n### ITERATION " + (i + 1)); println("#########################################################"); } final long start = System.currentTimeMillis(); final TestRunner runner = new TestRunner(conn); runner.runAll(testClasses, threaded, env); final long elapsed = System.currentTimeMillis() - start; println("Time to run tests: " + (elapsed / 1000) + " seconds" ); } } }