1 /*
   2  * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.management;
  27 
  28 import java.io.IOException;
  29 import java.nio.ByteBuffer;
  30 import java.nio.ByteOrder;
  31 import java.util.HashMap;
  32 import java.util.Iterator;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.concurrent.atomic.AtomicInteger;
  36 
  37 import jdk.internal.perf.Perf;
  38 import sun.management.counter.Units;
  39 import sun.management.counter.Counter;
  40 import sun.management.counter.perf.PerfInstrumentation;
  41 
  42 /**
  43  * A utility class to support the exporting and importing of the address
  44  * of a connector server using the instrumentation buffer.
  45  *
  46  * @since 1.5
  47  */
  48 public class ConnectorAddressLink {
  49     /**
  50      * A simple wrapper for the perf-counter backing {@linkplain ByteBuffer}
  51      */
  52     private static final class PerfHandle {
  53         private ByteBuffer bb;
  54 
  55         private PerfHandle(ByteBuffer bb) {
  56             this.bb = bb.order(ByteOrder.nativeOrder());
  57         }
  58 
  59         private void putLong(long l) {
  60             this.bb = bb.clear();
  61             this.bb.asLongBuffer().put(l);
  62         }
  63     }
  64 
  65     private static final String CONNECTOR_ADDRESS_COUNTER =
  66             "sun.management.JMXConnectorServer.address";
  67     private static final String REMOTE_CONNECTOR_STATE_COUNTER =
  68             "sun.management.JMXConnectorServer.remote.enabled";
  69 
  70     /*
  71      * The format of the jvmstat counters representing the properties of
  72      * a given out-of-the-box JMX remote connector will be as follows:
  73      *
  74      * sun.management.JMXConnectorServer.<counter>.<key>=<value>
  75      *
  76      * where:
  77      *
  78      *     counter = index computed by this class which uniquely identifies
  79      *               an out-of-the-box JMX remote connector running in this
  80      *               Java virtual machine.
  81      *     key/value = a given key/value pair in the map supplied to the
  82      *                 exportRemote() method.
  83      *
  84      * For example,
  85      *
  86      * sun.management.JMXConnectorServer.0.remoteAddress=service:jmx:rmi:///jndi/rmi://myhost:5000/jmxrmi
  87      * sun.management.JMXConnectorServer.0.authenticate=false
  88      * sun.management.JMXConnectorServer.0.ssl=false
  89      * sun.management.JMXConnectorServer.0.sslRegistry=false
  90      * sun.management.JMXConnectorServer.0.sslNeedClientAuth=false
  91      */
  92     private static final String REMOTE_CONNECTOR_COUNTER_PREFIX =
  93             "sun.management.JMXConnectorServer.";
  94 
  95     /*
  96      * JMX remote connector counter (it will be incremented every
  97      * time a new out-of-the-box JMX remote connector is created).
  98      */
  99     private static final AtomicInteger counter = new AtomicInteger();
 100 
 101     private static PerfHandle remotePerfHandle = null;
 102 
 103     /**
 104      * Exports the specified connector address to the instrumentation buffer
 105      * so that it can be read by this or other Java virtual machines running
 106      * on the same system.
 107      *
 108      * @param address The connector address.
 109      */
 110     public static void export(String address) {
 111         if (address == null || address.length() == 0) {
 112             throw new IllegalArgumentException("address not specified");
 113         }
 114         Perf perf = Perf.getPerf();
 115         perf.createString(
 116             CONNECTOR_ADDRESS_COUNTER, 1, Units.STRING.intValue(), address);
 117     }
 118 
 119     public static void unexportRemote() {
 120         unexport(remotePerfHandle);
 121     }
 122 
 123     private static void unexport(PerfHandle ph) {
 124         if (ph != null) {
 125             ph.putLong(-1L);
 126         }
 127     }
 128 
 129     /**
 130      * Imports the connector address from the instrument buffer
 131      * of the specified Java virtual machine.
 132      *
 133      * @param vmid an identifier that uniquely identifies a local Java virtual
 134      * machine, or <code>0</code> to indicate the current Java virtual machine.
 135      *
 136      * @return the value of the connector address, or <code>null</code> if the
 137      * target VM has not exported a connector address.
 138      *
 139      * @throws IOException An I/O error occurred while trying to acquire the
 140      * instrumentation buffer.
 141      */
 142     public static String importFrom(int vmid) throws IOException {
 143         Perf perf = Perf.getPerf();
 144         ByteBuffer bb;
 145         try {
 146             bb = perf.attach(vmid, "r");
 147         } catch (IllegalArgumentException iae) {
 148             throw new IOException(iae.getMessage());
 149         }
 150         List<Counter> counters =
 151                 new PerfInstrumentation(bb).findByPattern(CONNECTOR_ADDRESS_COUNTER);
 152         Iterator<Counter> i = counters.iterator();
 153         if (i.hasNext()) {
 154             Counter c = i.next();
 155             return (String) c.getValue();
 156         } else {
 157             return null;
 158         }
 159     }
 160 
 161     /**
 162      * Exports the specified remote connector address and associated
 163      * configuration properties to the instrumentation buffer so that
 164      * it can be read by this or other Java virtual machines running
 165      * on the same system.
 166      *
 167      * @param properties The remote connector address properties.
 168      */
 169     public static void exportRemote(Map<String, String> properties) {
 170         final int index = counter.getAndIncrement();
 171         Perf perf = Perf.getPerf();
 172         for (Map.Entry<String, String> entry : properties.entrySet()) {
 173             perf.createString(REMOTE_CONNECTOR_COUNTER_PREFIX + index + "." +
 174                     entry.getKey(), 1, Units.STRING.intValue(), entry.getValue());
 175         }
 176         if (remotePerfHandle != null) {
 177             remotePerfHandle.putLong(index);
 178         } else {
 179             remotePerfHandle = new PerfHandle(
 180                 perf.createLong(REMOTE_CONNECTOR_STATE_COUNTER, 1, Units.NONE.intValue(), (long)index)
 181             );
 182         }
 183     }
 184 
 185     /**
 186      * Imports the remote connector address and associated
 187      * configuration properties from the instrument buffer
 188      * of the specified Java virtual machine.
 189      *
 190      * @param vmid an identifier that uniquely identifies a local Java virtual
 191      * machine, or <code>0</code> to indicate the current Java virtual machine.
 192      *
 193      * @return a map containing the remote connector's properties, or an empty
 194      * map if the target VM has not exported the remote connector's properties.
 195      *
 196      * @throws IOException An I/O error occurred while trying to acquire the
 197      * instrumentation buffer.
 198      */
 199     public static Map<String, String> importRemoteFrom(int vmid) throws IOException {
 200         Perf perf = Perf.getPerf();
 201         ByteBuffer bb;
 202         try {
 203             bb = perf.attach(vmid, "r");
 204         } catch (IllegalArgumentException iae) {
 205             throw new IOException(iae.getMessage());
 206         }
 207         List<Counter> counters = new PerfInstrumentation(bb).getAllCounters();
 208         Map<String, String> properties = new HashMap<>();
 209         for (Counter c : counters) {
 210             String name =  c.getName();
 211             if (name.startsWith(REMOTE_CONNECTOR_COUNTER_PREFIX) &&
 212                     !name.equals(CONNECTOR_ADDRESS_COUNTER)) {
 213                 properties.put(name, c.getValue().toString());
 214             }
 215         }
 216         return properties;
 217     }
 218 }