--- /dev/null 2015-11-02 09:25:06.996292069 +0100 +++ new/test/sun/management/jmxremote/bootstrap/JMXInterfaceBindingTest.java 2015-11-02 11:05:30.443991039 +0100 @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2015, Red Hat Inc + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.rmi.ssl.SslRMIClientSocketFactory; + +/** + * Tests client connections to the JDK's built-in JMX agent server on the given + * ports/interface combinations. + * + * @see JMXInterfaceBindingSSLTest.sh + * @see JMXInterfaceBindingTest.sh + * + * @author Severin Gehwolf + * + * Usage: + * + * SSL: + * java -Dcom.sun.management.jmxremote.ssl.need.client.auth=true \ + * -Dcom.sun.management.jmxremote.host=127.0.0.1 \ + * -Dcom.sun.management.jmxremote.port=9111 \ + * -Dcom.sun.management.jmxremote.rmi.port=9112 \ + * -Dcom.sun.management.jmxremote.authenticate=false \ + * -Dcom.sun.management.jmxremote.ssl=true \ + * -Dcom.sun.management.jmxremote.registry.ssl=true + * -Djavax.net.ssl.keyStore=... \ + * -Djavax.net.ssl.keyStorePassword=... \ + * JMXInterfaceBindingTest 127.0.0.1 9111 9112 true + * + * Non-SSL: + * java -Dcom.sun.management.jmxremote.host=127.0.0.1 \ + * -Dcom.sun.management.jmxremote.port=9111 \ + * -Dcom.sun.management.jmxremote.rmi.port=9112 \ + * -Dcom.sun.management.jmxremote.authenticate=false \ + * -Dcom.sun.management.jmxremote.ssl=false \ + * JMXInterfaceBindingTest 127.0.0.1 9111 9112 false + * + */ +public class JMXInterfaceBindingTest { + + private final MainThread mainThread; + + public JMXInterfaceBindingTest(InetAddress bindAddress, + int jmxPort, + int rmiPort, + boolean useSSL) { + this.mainThread = new MainThread(bindAddress, jmxPort, rmiPort, useSSL); + } + + public void startEndpoint() { + mainThread.start(); + try { + mainThread.join(); + } catch (InterruptedException e) { + throw new RuntimeException("Test failed", e); + } + if (mainThread.isFailed()) { + mainThread.rethrowException(); + } + } + + public static void main(String[] args) { + if (args.length != 4) { + throw new RuntimeException( + "Test failed. usage: java JMXInterfaceBindingTest {true|false}"); + } + int jmxPort = parsePortFromString(args[1]); + int rmiPort = parsePortFromString(args[2]); + boolean useSSL = Boolean.parseBoolean(args[3]); + String strBindAddr = args[0]; + System.out.println( + "DEBUG: Running test for triplet (hostname,jmxPort,rmiPort) = (" + + strBindAddr + "," + jmxPort + "," + rmiPort + "), useSSL = " + useSSL); + InetAddress bindAddress; + try { + bindAddress = InetAddress.getByName(args[0]); + } catch (UnknownHostException e) { + throw new RuntimeException("Test failed. Unknown ip: " + args[0]); + } + JMXInterfaceBindingTest test = new JMXInterfaceBindingTest(bindAddress, + jmxPort, rmiPort, useSSL); + test.startEndpoint(); // Expect for shell script to terminate test + } + + private static int parsePortFromString(String port) { + try { + return Integer.parseInt(port); + } catch (NumberFormatException e) { + throw new RuntimeException( + "Invalid port specified. Not an integer! Value was: " + + port); + } + } + + private static class JMXConnectorThread extends Thread { + + private final InetAddress addr; + private final int jmxPort; + private final int rmiPort; + private final boolean useSSL; + private final CountDownLatch latch; + private boolean failed; + private boolean jmxConnectWorked; + private boolean rmiConnectWorked; + + private JMXConnectorThread(InetAddress addr, + int jmxPort, + int rmiPort, + boolean useSSL, + CountDownLatch latch) { + this.addr = addr; + this.jmxPort = jmxPort; + this.rmiPort = rmiPort; + this.latch = latch; + this.useSSL = useSSL; + } + + @Override + public void run() { + try { + connect(); + } catch (IOException e) { + failed = true; + } + } + + private void connect() throws IOException { + System.out.println( + "JMXConnectorThread: Attempting JMX connection on: " + + addr.getHostAddress() + " on port " + jmxPort); + JMXServiceURL url; + try { + url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + + addr.getHostAddress() + ":" + jmxPort + "/jmxrmi"); + } catch (MalformedURLException e) { + throw new RuntimeException("Test failed.", e); + } + Map env = new HashMap<>(); + if (useSSL) { + SslRMIClientSocketFactory csf = new SslRMIClientSocketFactory(); + env.put("com.sun.jndi.rmi.factory.socket", csf); + env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf); + } + // connect and immediately close + JMXConnector c = JMXConnectorFactory.connect(url, env); + c.close(); + System.out.println("JMXConnectorThread: connection to JMX worked"); + jmxConnectWorked = true; + checkRmiSocket(); + latch.countDown(); // signal we are done. + } + + private void checkRmiSocket() throws IOException { + Socket rmiConnection; + if (useSSL) { + rmiConnection = SSLSocketFactory.getDefault().createSocket(); + } else { + rmiConnection = new Socket(); + } + SocketAddress target = new InetSocketAddress(addr, rmiPort); + rmiConnection.connect(target); + if (useSSL) { + ((SSLSocket)rmiConnection).startHandshake(); + } + System.out.println( + "JMXConnectorThread: connection to rmi socket worked host/port = " + + addr.getHostAddress() + "/" + rmiPort); + rmiConnectWorked = true; + // Closing the channel without sending any data will cause an + // java.io.EOFException on the server endpoint. We don't care about this + // though, since we only want to test if we can connect. + rmiConnection.close(); + } + + public boolean isFailed() { + return failed; + } + + public boolean jmxConnectionWorked() { + return jmxConnectWorked; + } + + public boolean rmiConnectionWorked() { + return rmiConnectWorked; + } + } + + private static class MainThread extends Thread { + + private static final int WAIT_FOR_JMX_AGENT_TIMEOUT_MS = 500; + private final InetAddress bindAddress; + private final int jmxPort; + private final int rmiPort; + private final boolean useSSL; + private boolean terminated = false; + private boolean jmxAgentStarted = false; + private Exception excptn; + + private MainThread(InetAddress bindAddress, int jmxPort, int rmiPort, boolean useSSL) { + this.bindAddress = bindAddress; + this.jmxPort = jmxPort; + this.rmiPort = rmiPort; + this.useSSL = useSSL; + } + + @Override + public void run() { + try { + waitUntilReadyForConnections(); + // Do nothing, but wait for termination. + try { + while (!terminated) { + Thread.sleep(100); + } + } catch (InterruptedException e) { // ignore + } + System.out.println("MainThread: Thread stopped."); + } catch (Exception e) { + this.excptn = e; + } + } + + private void waitUntilReadyForConnections() { + CountDownLatch latch = new CountDownLatch(1); + JMXConnectorThread connectionTester = new JMXConnectorThread( + bindAddress, jmxPort, rmiPort, useSSL, latch); + connectionTester.start(); + boolean expired = false; + try { + expired = !latch.await(WAIT_FOR_JMX_AGENT_TIMEOUT_MS, TimeUnit.MILLISECONDS); + System.out.println( + "MainThread: Finished waiting for JMX agent to become available: expired == " + + expired); + jmxAgentStarted = !expired; + } catch (InterruptedException e) { + throw new RuntimeException("Test failed", e); + } + if (!jmxAgentStarted) { + throw new RuntimeException( + "Test failed. JMX server agents not becoming available."); + } + if (connectionTester.isFailed() + || !connectionTester.jmxConnectionWorked() + || !connectionTester.rmiConnectionWorked()) { + throw new RuntimeException( + "Test failed. JMX agent does not seem ready. See log output for details."); + } + // The shell script test expects this exact message being printed + System.out.println("MainThread: Ready for connections"); + } + + private boolean isFailed() { + return excptn != null; + } + + private void rethrowException() throws RuntimeException { + throw new RuntimeException(excptn); + } + } + +}