1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * 
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * The contents of this file are subject to the terms of either the Universal Permissive License
   7  * v 1.0 as shown at http://oss.oracle.com/licenses/upl
   8  *
   9  * or the following license:
  10  *
  11  * Redistribution and use in source and binary forms, with or without modification, are permitted
  12  * provided that the following conditions are met:
  13  * 
  14  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions
  15  * and the following disclaimer.
  16  * 
  17  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
  18  * conditions and the following disclaimer in the documentation and/or other materials provided with
  19  * the distribution.
  20  * 
  21  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to
  22  * endorse or promote products derived from this software without specific prior written permission.
  23  * 
  24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  26  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  27  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.rjmx.test;
  34 
  35 import java.io.IOException;
  36 import java.net.MalformedURLException;
  37 import java.util.Map;
  38 import java.util.Map.Entry;
  39 import java.util.Properties;
  40 
  41 import javax.management.MBeanServerConnection;
  42 import javax.management.remote.JMXServiceURL;
  43 import javax.security.auth.login.FailedLoginException;
  44 
  45 import org.junit.After;
  46 import org.junit.Assert;
  47 import org.junit.Assume;
  48 import org.junit.Before;
  49 import org.openjdk.jmc.common.test.MCTestCase;
  50 import org.openjdk.jmc.common.version.JavaVersionSupport;
  51 import org.openjdk.jmc.rjmx.ConnectionDescriptorBuilder;
  52 import org.openjdk.jmc.rjmx.ConnectionException;
  53 import org.openjdk.jmc.rjmx.ConnectionToolkit;
  54 import org.openjdk.jmc.rjmx.IConnectionDescriptor;
  55 import org.openjdk.jmc.rjmx.IConnectionHandle;
  56 import org.openjdk.jmc.rjmx.IServerHandle;
  57 import org.openjdk.jmc.rjmx.ServiceNotAvailableException;
  58 import org.openjdk.jmc.rjmx.services.IDiagnosticCommandService;
  59 import org.openjdk.jmc.rjmx.subscription.IMBeanHelperService;
  60 import org.openjdk.jmc.rjmx.subscription.IMRIMetadataService;
  61 import org.openjdk.jmc.rjmx.subscription.ISubscriptionService;
  62 
  63 /**
  64  */
  65 public class RjmxTestCase extends MCTestCase {
  66         /**
  67          * The host running the management server.
  68          */
  69         public final static String PROPERTY_RJMX_HOST = "jmc.test.rjmx.host";
  70 
  71         /**
  72          * The port of the management server. (Used for both JMX over RMI and RMP.)
  73          */
  74         public final static String PROPERTY_RJMX_PORT = "jmc.test.rjmx.port";
  75 
  76         /**
  77          * Boolean option to use RMP to talk to the management server. (False means to use JMX over
  78          * RMI.)
  79          */
  80         public final static String PROPERTY_RJMX_RMP = "jmc.test.rjmx.rmp";
  81 
  82         /**
  83          * The service URL to the management server. (If set, has precedence over host, port and
  84          * protocol.)
  85          */
  86         public final static String PROPERTY_JMX_SERVICE_URL = "jmc.test.rjmx.serviceURL";
  87 
  88         /**
  89          * The default host to test against.
  90          */
  91         public final static String DEFAULT_HOST = "localhost";
  92 
  93         protected String m_host;
  94         protected IConnectionHandle m_connectionHandle;
  95         protected IConnectionDescriptor m_connectionDescriptor;
  96 
  97         protected boolean isLocal14 = false;
  98 
  99         /**
 100          * Do not change access. Use {@link #getDefaultConnectionDescriptor()} instead.
 101          */
 102         private static volatile IConnectionDescriptor SHARED_DESCRIPTOR;
 103 
 104         /**
 105          * Obtain a RJMX ConnectionDescriptor for the server to run tests against. The descriptor is
 106          * formed by taking "jmc.test.rjmx.*" properties and the JDK level of the current JVM into
 107          * account. If more than one is possible, attempts to probe once.
 108          *
 109          * @return The ConnectionDescriptor
 110          * @throws MalformedURLException
 111          */
 112         public static IConnectionDescriptor getDefaultConnectionDescriptor() throws MalformedURLException {
 113                 if (SHARED_DESCRIPTOR == null) {
 114                         String serviceURL = System.getProperty(PROPERTY_JMX_SERVICE_URL);
 115                         if (serviceURL != null) {
 116                                 SHARED_DESCRIPTOR = new ConnectionDescriptorBuilder().url(new JMXServiceURL(serviceURL)).build();
 117                         } else {
 118                                 String host = System.getProperty(PROPERTY_RJMX_HOST, DEFAULT_HOST);
 119                                 int jmxPort = Integer.getInteger(PROPERTY_RJMX_PORT, ConnectionDescriptorBuilder.DEFAULT_PORT)
 120                                                 .intValue();
 121                                 IConnectionDescriptor candidate = new ConnectionDescriptorBuilder().hostName(host).port(jmxPort)
 122                                                 .build();
 123                                 SHARED_DESCRIPTOR = candidate;
 124                         }
 125                 }
 126                 return SHARED_DESCRIPTOR;
 127         }
 128 
 129         protected static boolean probe(IConnectionDescriptor descriptor) {
 130                 long start = System.currentTimeMillis();
 131                 try {
 132                         System.out.println("Probing Service URL " + descriptor.createJMXServiceURL() + " ...");
 133                         IConnectionHandle handle = createConnectionHandle(descriptor);
 134                         long up = System.currentTimeMillis();
 135                         System.out.println("... connected in " + (up - start) + " ms ...");
 136                         // Just in case we fail ...
 137                         start = up;
 138                         handle.close();
 139                         long down = System.currentTimeMillis();
 140                         System.out.println("... closed in " + (down - start) + " ms.");
 141                         return true;
 142                 } catch (Exception e) {
 143                         long fail = System.currentTimeMillis();
 144                         System.out.println("... failed in " + (fail - start) + " ms.");
 145                         return false;
 146                 }
 147         }
 148 
 149         /**
 150          * The {@link IConnectionDescriptor} to run the test against. Instance method so it can be
 151          * overridden. Mainly intended for tests suites that run against multiple JVMs.
 152          *
 153          * @return The ConnectionDescriptor
 154          * @throws MalformedURLException
 155          */
 156         protected IConnectionDescriptor getTestConnectionDescriptor() throws MalformedURLException {
 157                 return getDefaultConnectionDescriptor();
 158         }
 159 
 160         /**
 161          * Adds all system properties from the given connector to props with the given prefix. No
 162          * parameters are allowed to be null.
 163          *
 164          * @param connector
 165          * @param props
 166          * @param prefix
 167          *            prefix to use for server properties
 168          * @throws Exception
 169          */
 170         public static void getServerProperties(IConnectionHandle connector, Properties props, String prefix)
 171                         throws Exception {
 172                 System.out.println("Retrieving system properties (prefixed with '" + prefix + "') ...");
 173                 MBeanServerConnection server = connector.getServiceOrThrow(MBeanServerConnection.class);
 174                 Map<String, String> systemProperties = ConnectionToolkit.getRuntimeBean(server).getSystemProperties();
 175                 if (systemProperties != null) {
 176                         for (Entry<String, String> e : systemProperties.entrySet()) {
 177                                 props.setProperty(prefix + e.getKey(), e.getValue());
 178                         }
 179                 } else {
 180                         System.out.println("Could not retrieve system properties");
 181                 }
 182         }
 183 
 184         /**
 185          * Adds all system properties from the given ConnectionDescriptor to props with the given
 186          * prefix. No parameters are allowed to be null.
 187          *
 188          * @param connDesc
 189          * @param props
 190          * @param prefix
 191          *            prefix to use for server properties
 192          * @throws Exception
 193          */
 194         public static void getServerProperties(IConnectionDescriptor connDesc, Properties props, String prefix)
 195                         throws Exception {
 196                 System.out.println("Connecting to " + connDesc.createJMXServiceURL() + " ...");
 197                 IConnectionHandle connectionHandle = createConnectionHandle(connDesc);
 198                 getServerProperties(connectionHandle, props, prefix);
 199                 System.out.println("Disconnecting ...");
 200                 connectionHandle.close();
 201         }
 202 
 203         /**
 204          * @see org.junit.Test#Before
 205          */
 206         @Before
 207         public synchronized void mcTestCaseBefore() throws Exception {
 208                 m_connectionDescriptor = getTestConnectionDescriptor();
 209                 m_host = ConnectionToolkit.getHostName(m_connectionDescriptor.createJMXServiceURL());
 210                 m_connectionHandle = createConnectionHandle(m_connectionDescriptor);
 211                 Assert.assertTrue(m_connectionHandle.isConnected());
 212         }
 213 
 214         /**
 215          * Quick'n'Dirty way to create a {@link IConnectionHandle}.
 216          *
 217          * @return an {@link IConnectionHandle}
 218          * @throws FailedLoginException
 219          * @throws ConnectionException
 220          */
 221         private static IConnectionHandle createConnectionHandle(IConnectionDescriptor descriptor)
 222                         throws IOException, FailedLoginException, ConnectionException {
 223                 return IServerHandle.create(descriptor).connect("Test");
 224         }
 225 
 226         /**
 227          * @see org.junit.Test#After
 228          */
 229         @After
 230         public synchronized void mcTestCaseAfter() throws Exception {
 231                 if (m_connectionHandle != null) {
 232                         m_connectionHandle.close();
 233                         m_connectionHandle = null;
 234                 }
 235         }
 236 
 237         protected MBeanServerConnection getMBeanServerConnection()
 238                         throws ConnectionException, ServiceNotAvailableException {
 239                 return m_connectionHandle.getServiceOrThrow(MBeanServerConnection.class);
 240         }
 241 
 242         protected IMBeanHelperService getMBeanHelperService() throws ConnectionException, ServiceNotAvailableException {
 243                 return m_connectionHandle.getServiceOrThrow(IMBeanHelperService.class);
 244         }
 245 
 246         protected IMRIMetadataService getMRIMetadataService() throws ConnectionException, ServiceNotAvailableException {
 247                 return m_connectionHandle.getServiceOrThrow(IMRIMetadataService.class);
 248         }
 249 
 250         protected ISubscriptionService getAttributeSubscriptionService()
 251                         throws ConnectionException, ServiceNotAvailableException {
 252                 return m_connectionHandle.getServiceOrThrow(ISubscriptionService.class);
 253         }
 254 
 255         protected IDiagnosticCommandService getDiagnosticCommandService()
 256                         throws ConnectionException, ServiceNotAvailableException {
 257                 assumeHasDiagnosticCommandsService(m_connectionHandle);
 258                 return m_connectionHandle.getServiceOrThrow(IDiagnosticCommandService.class);
 259         }
 260 
 261         protected IConnectionHandle getConnectionHandle() {
 262                 return m_connectionHandle;
 263         }
 264 
 265         protected void assumeHotSpot8OrLater(IConnectionHandle handle) {
 266                 Assume.assumeTrue("This test assumes JDK 8 (HotSpot 25) or later!", ConnectionToolkit.isHotSpot(handle)
 267                                 && ConnectionToolkit.isJavaVersionAboveOrEqual(handle, JavaVersionSupport.JDK_8));
 268         }
 269 
 270         protected void assumeHotSpot7u4OrLater(IConnectionHandle handle) {
 271                 Assume.assumeTrue("This test assumes JDK 7u4 (HotSpot 23) or later!", ConnectionToolkit.isHotSpot(handle)
 272                                 && ConnectionToolkit.isJavaVersionAboveOrEqual(handle, JavaVersionSupport.JDK_7_U_4));
 273         }
 274 
 275         protected void assumeHotSpot7u12OrLater(IConnectionHandle handle) {
 276                 Assume.assumeTrue("This test assumes JDK 7u12 (HotSpot 24) or later!", ConnectionToolkit.isHotSpot(handle)
 277                                 && ConnectionToolkit.isJavaVersionAboveOrEqual(handle, JavaVersionSupport.JDK_7_U_40));
 278         }
 279 
 280         protected void assumeHasDiagnosticCommandsService(IConnectionHandle handle) {
 281                 Assume.assumeTrue("This test needs a working diagnostic commands service!",
 282                                 handle.hasService(IDiagnosticCommandService.class));
 283         }
 284 }