View Javadoc
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         StringBuffer sb = new StringBuffer();
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 }