Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8173180

VirtualMachine.startLocalManagementAgent() returns URI with unreliable IP address

XMLWordPrintable

      FULL PRODUCT VERSION :
      java version "1.8.0_112"
      Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
      Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Tested on Windows 7 and Windows 10

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      Issue comes out if IP address of machine changes or there is a local IP that cannot be used by any normal application (e.g.: Virtualbox has a virtual network with an IP address on the host machine)

      A DESCRIPTION OF THE PROBLEM :
      If we call com.sun.tools.attach.VirtualMachine.startLocalManagementAgent() function, we get an URI that does not always work.

      The returned URI looks like this:

          service:jmx:rmi://127.0.0.1/stub/rO0ABXN9AAAAAQAlamF2YXgubWFuYWdlbWVudC5yZW1vdGUucm1pLlJNSVNlcnZlcnhyABdqYXZhLmxhbmcucmVmbG...

      However, the IP address 127.0.0.1 is not used from it. Instead, the stub is used that contains an object like the following:

          Proxy[RMIServer,RemoteObjectInvocationHandler[UnicastRef2 [liveRef: [endpoint:[169.254.28.249:49803](remote),objID:[-11d647db:159b8545f64:-7fff, -2646397260677366721]]]]]

      The IP listed in this URI is often not usable. Use-cases:

      If you install Virtualbox and create a virtual network, it will have an IP address on the host computer. If this IP address is returned in the STUB object (that likely happens) the connection will fail.

      If a real IP of the computer is returned, but the IP is changed or lost later (e.g.: changed wi-fi network), calling the startLocalManagementAgent() on the same VirtualMachine instance will return the same old URI. Therefore we will not be able to connect that VirtualMachine anymore.

      In my opinion, the stub should contain the loopback IP address of the computer so the issue never will happen.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
       - Execute VirtualMachine.startLocalManagementAgent()
       - Disable the network adapter that is in the stub (you can find out the IP by deserializing the object and calling toString() on it)
       - Call VirtualMachine.startLocalManagementAgent() again, the same IP will be returned.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      I expect that the loopback IP address should be used instead of a "random" IP address even if that IP address is not available anymore in the stub or the URI.
      ACTUAL -
      A "random" IP address is used that might not even be available anymore or it is not a local IP (but the IP of a virtual network)

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      java.rmi.ConnectIOException: Exception creating connection to: 169.254.28.249; nested exception is:
      java.net.SocketException: Network is unreachable: connect
      at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:631)
      at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
      at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
      at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:130)
      at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:227)
      at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:179)
      at com.sun.proxy.$Proxy0.newClient(Unknown Source)
      at javax.management.remote.rmi.RMIConnector.getConnection(RMIConnector.java:2430)
      at javax.management.remote.rmi.RMIConnector.connect(RMIConnector.java:308)
      at javax.management.remote.JMXConnectorFactory.connect(JMXConnectorFactory.java:270)
      at javax.management.remote.JMXConnectorFactory.connect(JMXConnectorFactory.java:229)
      at test.test.TestLocalJMXServiceURL.main(TestLocalJMXServiceURL.java:40)
      Caused by: java.net.SocketException: Network is unreachable: connect
      at java.net.DualStackPlainSocketImpl.connect0(Native Method)
      at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
      at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
      at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
      at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
      at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
      at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
      at java.net.Socket.connect(Socket.java:589)
      at java.net.Socket.connect(Socket.java:538)
      at java.net.Socket.<init>(Socket.java:434)
      at java.net.Socket.<init>(Socket.java:211)
      at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
      at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:148)
      at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
      ... 11 more

      REPRODUCIBILITY :
      This bug can be reproduced occasionally.

      ---------- BEGIN SOURCE ----------
        public static void main(final String[] args) {
          try {
            VirtualMachine virtualMachine = VirtualMachine.attach("PID");
            String serviceURL = virtualMachine.startLocalManagementAgent();

            // Let's see what is in the url
            System.out.println(serviceURL);

            int indexOfStub = serviceURL.indexOf("stub/");
            if (indexOfStub >= 0) {
              String objectBase64 = serviceURL.substring(indexOfStub + 5);
              byte[] objectBA = Base64.getDecoder().decode(objectBase64);
              ByteArrayInputStream bin = new ByteArrayInputStream(objectBA);
              ObjectInputStream oin = new ObjectInputStream(bin);
              Object object = oin.readObject();

              // Let's see what IP address is used
              System.out.println(object);
            }

            JMXServiceURL jmxServiceURL = new JMXServiceURL(serviceURL);

            // Failure comes here if IP address is changed or it is a Virtualbox virtual network IP.
            JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL);
            jmxConnector.close();
          } catch (AttachNotSupportedException | IOException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }

        }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Install a loopback network adapter for windows and set the metric of all adapters manually for all adapters in the way that Java will return the IP address of the loopback adapter.

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: