/*
 * Copyright 2010 Proofpoint, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.proofpoint.jmx;

import com.google.common.net.HostAndPort;
import com.proofpoint.log.Logger;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.net.MalformedURLException;
import java.util.Properties;

class JmxAgent9
        implements JmxAgent
{
    private static final Logger log = Logger.get(JmxAgent.class);

    private final JMXServiceURL url;

    @Inject
    public JmxAgent9(JmxConfig config)
            throws IOException
    {
        if (config.isEnabled()) {
            int registryPort;
            if (config.getRmiRegistryPort() == null) {
                registryPort = NetUtils.findUnusedPort();
            }
            else {
                registryPort = config.getRmiRegistryPort();
            }

            int serverPort = 0;
            if (config.getRmiServerPort() != null) {
                serverPort = config.getRmiServerPort();
            }

            System.setProperty("java.rmi.server.hostname", "127.0.0.1");

            try {
                VirtualMachine virtualMachine = VirtualMachine.attach(Long.toString(getProcessId()));
                try {
                    virtualMachine.startLocalManagementAgent();

                    Properties properties = new Properties();
                    properties.setProperty("com.sun.management.jmxremote.port", Integer.toString(registryPort));
                    properties.setProperty("com.sun.management.jmxremote.rmi.port", Integer.toString(serverPort));
                    properties.setProperty("com.sun.management.jmxremote.authenticate", "false");
                    properties.setProperty("com.sun.management.jmxremote.ssl", "false");
                    virtualMachine.startManagementAgent(properties);
                }
                finally {
                    virtualMachine.detach();
                }
            }
            catch (AttachNotSupportedException e) {
                throw new RuntimeException(e);
            }

            HostAndPort address;
            try {
                // This is how the jdk jmx agent constructs its url
                JMXServiceURL url = new JMXServiceURL("rmi", null, registryPort);
                address = HostAndPort.fromParts(url.getHost(), url.getPort());
            }
            catch (MalformedURLException e) {
                // should not happen...
                throw new AssertionError(e);
            }

            log.info("JMX agent started and listening on %s", address);


            this.url = new JMXServiceURL(String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", address.getHost(), address.getPort()));
        }
        else {
            this.url = null;
        }
    }

    @Nullable
    @Override
    public JMXServiceURL getUrl()
    {
        return url;
    }

    private static long getProcessId()
    {
        // TODO: replace with ProcessHandle.current().getPid() in Java 9
        String name = ManagementFactory.getRuntimeMXBean().getName();
        int index = name.indexOf('@');

        if (index < 1) {
            throw new AssertionError("Cannot get process PID");
        }

        try {
            return Long.parseLong(name.substring(0, index));
        }
        catch (NumberFormatException e) {
            throw new AssertionError("Cannot get process PID");
        }
    }

    public static void main(String[] args)
            throws IOException
    {
        new JmxAgent9(new JmxConfig());
        new JmxAgent9(new JmxConfig());
    }
}

@@ -1052,16 +1052,56 @@
 rties;%0A%0A
+import static java.lang.String.format;%0A%0A
 class Jm
@@ -1201,24 +1201,107 @@
 nt.class);%0A%0A
+    private static final String ALLOW_SELF_ATTACH = %22jdk.attach.allowAttachSelf%22;%0A%0A
     private 
@@ -2907,32 +2907,309 @@
 );%0A            %7D
+%0A            catch (IOException e) %7B%0A                if (!Boolean.getBoolean(ALLOW_SELF_ATTACH)) %7B%0A                    throw new IOException(format(%22%25s (try adding '-D%25s=true' to the JVM config)%22, e, ALLOW_SELF_ATTACH));%0A                %7D%0A                throw e;%0A            %7D
 %0A%0A            Ho
@@ -3749,23 +3749,16 @@
 viceURL(
-String.
 format(%22

Add self attach hint when JMX agent start fails