1 /*
   2  * Copyright (c) 2004, 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.jvmstat.monitor;
  27 
  28 import java.net.*;
  29 
  30 /**
  31  * An abstraction that identifies a target Java Virtual Machine.
  32  * The VmIdentifier, or vmid, provides a convenient string representation
  33  * of the information needed to locate and communicate with a target
  34  * Java Virtual Machine. The string, based on a {@link URI}, may specify
  35  * the communications protocol, host name, local vm identifier, and protocol
  36  * specific information for a target Java Virtual Machine. The format for
  37  * a VmIdentifier string is:
  38  * <pre>
  39  *      [<I>protocol</I>:][<I>//</I>]<I><B>lvmid</B></I>[<I>@hostname</I>][<I>:port</I>][<I>/servername</I>]
  40  * </pre>
  41  * The only required component of this string is the Local Virtual Machine
  42  * Identifier, or {@code lvmid}, which uniquely identifies the target
  43  * Java Virtual Machine on a host. The optional components of the VmIdentifier
  44  * include:
  45  * <ul>
  46  *   <li>{@code protocol} - The communications protocol. A VmIdentifier
  47  *       omitting the protocol must be resolved against a HostIdentifier
  48  *       using {@link HostIdentifier#resolve}.
  49  *       </li>
  50  *   <li>{@code hostname} - A hostname or IP address indicating the target
  51  *       host. A VmIdentifier omitting the protocol must be resolved
  52  *       against a HostIdentifier using {@link HostIdentifier#resolve}.
  53  *       </li>
  54  *   <li>{@code port} - The port for the communications protocol.
  55  *       Treatment of the {@code port} parameter is implementation
  56  *       (protocol) specific. A VmIdentifier omitting the protocol should
  57  *       be resolved against a HostIdentifier using
  58  *       {@link HostIdentifier#resolve}.
  59  *       </li>
  60  *   <li>{@code servername} - The treatment of the Path, Query, and
  61  *       Fragment components of the VmIdentifier are implementation
  62  *       (protocol) dependent. A VmIdentifier omitting the protocol should
  63  *       be resolved against a HostIdentifier using
  64  *       {@link HostIdentifier#resolve}.
  65  *       </li>
  66  * </ul>
  67  * <p>
  68  * All VmIdentifier instances are constructed as absolute, hierarchical URIs.
  69  * The constructors will accept relative (and even some malformed,
  70  * though convenient) URI strings. Such strings are transformed into
  71  * legitimate, absolute URI strings.
  72  * <p>
  73  * With the exception of <em>file:</em> based VmIdentifier strings, all
  74  * VmIdentifier strings must include a {@code lvmid}. Attempting to construct
  75  * a non-file based VmIdentifier that doesn't include a {@code lvmid}
  76  * component will result in a {@code MonitorException}.
  77  * <p>
  78  * Here are some examples of VmIdentifier strings.
  79  * <ul>
  80  *    <li>Relative URIs
  81  *      <ul>
  82  *         <li><em>1234</em> - Specifies the Java Virtual Machine
  83  *             identified by lvmid <em>1234</em> on an unnamed host.
  84  *             This string is transformed into the absolute form
  85  *             <em>//1234</em>, which must be resolved against a
  86  *             HostIdentifier.
  87  *         </li>
  88  *         <li><em>1234@hostname</em> - Specifies the Java Virtual
  89  *             Machine identified by lvmid <em>1234</em> on host
  90  *             <em>hostname</em> with an unnamed protocol.
  91  *             This string is transformed into the absolute form
  92  *             <em>//1234@hostname</em>, which must be resolved against
  93  *             a HostIdentifier.
  94  *         </li>
  95  *         <li><em>1234@hostname:2099</em> - Specifies the Java Virtual
  96  *             Machine identified by lvmid <em>1234</em> on host
  97  *             <em>hostname</em> with an unnamed protocol, but with
  98  *             port <em>2099</em>. This string is transformed into
  99  *             the absolute form <em>//1234@hostname:2099</em>, which
 100  *             must be resolved against a HostIdentifier.
 101  *         </li>
 102  *      </ul>
 103  *    </li>
 104  *    <li>Absolute URIs
 105  *      <ul>
 106  *         <li><em>rmi://1234@hostname:2099/remoteobjectname</em> -
 107  *             Specifies the Java Virtual Machine identified by lvmid
 108  *             <em>1234</em> on host <em>hostname</em> accessed
 109  *             using the <em>rmi:</em> protocol through the rmi remote
 110  *             object named <em>remoteobjectname</em> as registered with
 111  *             the <em>rmiserver</em> on port <em>2099</em> on host
 112  *             <em>hostname</em>.
 113  *         </li>
 114  *         <li><em>file:/path/file</em> - Identifies a Java Virtual Machine
 115  *             through accessing a special file based protocol to use as
 116  *             the communications mechanism.
 117  *         </li>
 118  *      </ul>
 119  *    </li>
 120  * </ul>
 121  *
 122  * @see URI
 123  * @see HostIdentifier
 124  * @author Brian Doherty
 125  * @since 1.5
 126  */
 127 public class VmIdentifier {
 128     private URI uri;
 129 
 130     /**
 131      * creates a canonical representation of the uriString. This method
 132      * performs certain translations depending on the type of URI generated
 133      * by the string.
 134      */
 135     private URI canonicalize(String uriString) throws URISyntaxException {
 136         if (uriString == null) {
 137             uriString = "local://0@localhost";
 138             return new URI(uriString);
 139         }
 140 
 141         URI u = new URI(uriString);
 142 
 143         if (u.isAbsolute()) {
 144             if (u.isOpaque()) {
 145                 /*
 146                  * rmi:1234@hostname/path#fragment converted to
 147                  * rmi://1234@hostname/path#fragment
 148                  */
 149                 u = new URI(u.getScheme(), "//" + u.getSchemeSpecificPart(),
 150                             u.getFragment());
 151             }
 152         } else {
 153             /*
 154              * make the uri absolute, if possible. A relative URI doesn't
 155              * specify the scheme part, so it's safe to prepend a "//" and
 156              * try again.
 157              */
 158             if (!uriString.startsWith("//")) {
 159                 if (u.getFragment() == null) {
 160                     u = new URI("//" + u.getSchemeSpecificPart());
 161                 } else {
 162                     u = new URI("//" + u.getSchemeSpecificPart() + "#"
 163                                 + u.getFragment());
 164                 }
 165             }
 166         }
 167         return u;
 168     }
 169 
 170     /**
 171      * check that the VmIdentifier includes a unique numerical identifier
 172      * for the target JVM.
 173      */
 174     private void validate() throws URISyntaxException {
 175         // file:// uri, which is a special case where the lvmid is not required.
 176         String s = getScheme();
 177         if ((s != null) && (s.compareTo("file") == 0)) {
 178             return;
 179         }
 180         if (getLocalVmId() == -1) {
 181             throw new URISyntaxException(uri.toString(), "Local vmid required");
 182         }
 183     }
 184 
 185     /**
 186      * Create a VmIdentifier instance from a string value.
 187      *
 188      * @param uriString a string representing a target Java Virtual Machine.
 189      *                  The syntax of the string must conforms to the rules
 190      *                  specified in the class documentation.
 191      * @throws URISyntaxException Thrown when the uriString or its canonical
 192      *                            form is poorly formed.
 193      */
 194     public VmIdentifier(String uriString) throws URISyntaxException {
 195         URI u;
 196         try {
 197             u = canonicalize(uriString);
 198         } catch (URISyntaxException e) {
 199             /*
 200              * a vmid of the form 1234@hostname:1098 causes an exception,
 201              * so try again with a leading "//"
 202              */
 203             if (uriString.startsWith("//")) {
 204                 throw e;
 205             }
 206             u = canonicalize("//"+uriString);
 207         }
 208 
 209         uri = u;
 210 
 211         // verify that we have a valid lvmid
 212         validate();
 213     }
 214 
 215     /**
 216      * Create a VmIdentifier instance from a URI object.
 217      *
 218      * @param uri a well formed, absolute URI indicating the
 219      *            target Java Virtual Machine.
 220      * @throws URISyntaxException Thrown if the URI is missing some
 221      *                            required component.
 222      */
 223     public VmIdentifier(URI uri) throws URISyntaxException {
 224         this.uri = uri;
 225         validate();
 226     }
 227 
 228     /**
 229      * Return the corresponding HostIdentifier for this VmIdentifier.
 230      * <p>
 231      * This method constructs a HostIdentifier object from the VmIdentifier.
 232      * If the VmIdentifier is not specific about the protocol or other
 233      * components of the URI, then the resulting HostIdentifier will
 234      * be constructed based on this missing information. Typically, the
 235      * missing components will have result in the HostIdentifier assigning
 236      * assumed defaults that allow the VmIdentifier to be resolved according
 237      * to those defaults.
 238      * <p>
 239      * For example, a VmIdentifier that specifies only a {@code lvmid}
 240      * will result in a HostIdentifier for <em>localhost</em> utilizing
 241      * the default local protocol, <em>local:</em>. A VmIdentifier that
 242      * specifies both a {@code vmid} and a {@code hostname} will result
 243      * in a HostIdentifier for the specified host with the default remote
 244      * protocol, <em>rmi:</em>, using the protocol defaults for the
 245      * {@code port} and {@code servername} components.
 246      *
 247      * @return HostIdentifier - the host identifier for the host containing
 248      *                          the Java Virtual Machine represented by this
 249      *                          VmIdentifier.
 250      * @throws URISyntaxException Thrown if a bad host URI is constructed.
 251      *                            This exception may get encapsulated into
 252      *                            a MonitorException in a future version.
 253      */
 254     public HostIdentifier getHostIdentifier() throws URISyntaxException {
 255         StringBuilder sb = new StringBuilder();
 256         if (getScheme() != null) {
 257             sb.append(getScheme()).append(":");
 258         }
 259         sb.append("//").append(getHost());
 260         if (getPort() != -1) {
 261             sb.append(":").append(getPort());
 262         }
 263         if (getPath() != null) {
 264             sb.append(getPath());
 265         }
 266         return new HostIdentifier(sb.toString());
 267     }
 268 
 269     /**
 270      * Return the Scheme, or protocol, portion of this VmIdentifier.
 271      *
 272      * @return String - the scheme for this VmIdentifier.
 273      * @see URI#getScheme()
 274      */
 275     public String getScheme() {
 276         return uri.getScheme();
 277     }
 278 
 279     /**
 280      * Return the Scheme Specific Part of this VmIdentifier.
 281      *
 282      * @return String - the Scheme Specific Part for this VmIdentifier.
 283      * @see URI#getSchemeSpecificPart()
 284      */
 285     public String getSchemeSpecificPart() {
 286         return uri.getSchemeSpecificPart();
 287     }
 288 
 289     /**
 290      * Return the UserInfo part of this VmIdentifier.
 291      *
 292      * @return String - the UserInfo part for this VmIdentifier.
 293      * @see URI#getUserInfo()
 294      */
 295     public String getUserInfo() {
 296         return uri.getUserInfo();
 297     }
 298 
 299     /**
 300      * Return the Host part of this VmIdentifier.
 301      *
 302      * @return String - the Host part for this VmIdentifier.
 303      * @see URI#getHost()
 304      */
 305     public String getHost() {
 306         return uri.getHost();
 307     }
 308 
 309     /**
 310      * Return the Port part of this VmIdentifier.
 311      *
 312      * @return int - the Port part for this VmIdentifier.
 313      * @see URI#getPort()
 314      */
 315     public int getPort() {
 316         return uri.getPort();
 317     }
 318 
 319     /**
 320      * Return the Authority part of this VmIdentifier.
 321      *
 322      * @return String - the Authority part for this VmIdentifier.
 323      * @see URI#getAuthority()
 324      */
 325     public String getAuthority() {
 326         return uri.getAuthority();
 327     }
 328 
 329     /**
 330      * Return the Path part of this VmIdentifier.
 331      *
 332      * @return String - the Path part for this VmIdentifier.
 333      * @see URI#getPath()
 334      */
 335     public String getPath() {
 336         return uri.getPath();
 337     }
 338 
 339     /**
 340      * Return the Query part of this VmIdentifier.
 341      *
 342      * @return String - the Query part for this VmIdentifier.
 343      * @see URI#getQuery()
 344      */
 345     public String getQuery() {
 346         return uri.getQuery();
 347     }
 348 
 349     /**
 350      * Return the Fragment part of this VmIdentifier.
 351      *
 352      * @return String - the Fragment part for this VmIdentifier.
 353      * @see URI#getFragment()
 354      */
 355     public String getFragment() {
 356         return uri.getFragment();
 357     }
 358 
 359     /**
 360      * Return the Local Virtual Machine Identifier for this VmIdentifier.
 361      * The Local Virtual Machine Identifier is also known as the
 362      * <em>lvmid</em>.
 363      *
 364      * @return int - the lvmid for this VmIdentifier.
 365      */
 366     public int getLocalVmId() {
 367         int result = -1;
 368         try {
 369             if (uri.getUserInfo() == null) {
 370                 result = Integer.parseInt(uri.getAuthority());
 371             } else {
 372                 result = Integer.parseInt(uri.getUserInfo());
 373             }
 374         } catch (NumberFormatException e) { }
 375         return result;
 376     }
 377 
 378     /**
 379      * Return the mode indicated in this VmIdentifier.
 380      *
 381      * @return String - the mode string. If no mode is specified, then "r"
 382      *                  is returned. otherwise, the specified mode is returned.
 383      */
 384     public String getMode() {
 385         String query = getQuery();
 386         if (query != null) {
 387             String[] queryArgs = query.split("\\+");
 388             for (int i = 0; i < queryArgs.length; i++) {
 389                 if (queryArgs[i].startsWith("mode=")) {
 390                     int index = queryArgs[i].indexOf('=');
 391                     return queryArgs[i].substring(index+1);
 392                 }
 393             }
 394         }
 395         return "r";
 396     }
 397 
 398     /**
 399      * Return the URI associated with the VmIdentifier.
 400      *
 401      * @return URI - the URI.
 402      * @see URI
 403      */
 404     public URI getURI() {
 405         return uri;
 406     }
 407 
 408     /**
 409      * Return the hash code for this VmIdentifier. The hash code is
 410      * identical to the hash code for the contained URI.
 411      *
 412      * @return int - the hashcode.
 413      * @see URI#hashCode()
 414      */
 415     public int hashCode() {
 416         return uri.hashCode();
 417     }
 418 
 419     /**
 420      * Test for quality with other objects.
 421      *
 422      * @param object the object to be test for equality.
 423      * @return boolean - returns true if the given object is of type
 424      *                   VmIdentifier and its URI field is equal to
 425      *                   this object's URI field. Otherwise, return false.
 426      *
 427      * @see URI#equals(Object)
 428      */
 429     public boolean equals(Object object) {
 430         if (object == this) {
 431             return true;
 432         }
 433         if (!(object instanceof VmIdentifier)) {
 434             return false;
 435         }
 436         return uri.equals(((VmIdentifier)object).uri);
 437     }
 438 
 439     /**
 440      * Convert to a string representation. Conversion is identical to
 441      * calling getURI().toString(). This may change in a future release.
 442      *
 443      * @return String - a String representation of the VmIdentifier.
 444      *
 445      * @see URI#toString()
 446      */
 447     public String toString() {
 448         return uri.toString();
 449     }
 450 }