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