1 /* 2 * Copyright (c) 2002, 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 27 package javax.management.remote; 28 29 30 import com.sun.jmx.remote.util.ClassLogger; 31 import com.sun.jmx.remote.util.EnvHelp; 32 import java.io.IOException; 33 import java.io.InvalidObjectException; 34 import java.io.ObjectInputStream; 35 36 import java.io.Serializable; 37 import java.net.InetAddress; 38 import java.net.MalformedURLException; 39 import java.net.UnknownHostException; 40 import java.util.BitSet; 41 import java.util.Locale; 42 import java.util.StringTokenizer; 43 44 /** 45 * <p>The address of a JMX API connector server. Instances of this class 46 * are immutable.</p> 47 * 48 * <p>The address is an <em>Abstract Service URL</em> for SLP, as 49 * defined in RFC 2609 and amended by RFC 3111. It must look like 50 * this:</p> 51 * 52 * <blockquote> 53 * 54 * <code>service:jmx:<em>protocol</em>:<em>sap</em></code> 55 * 56 * </blockquote> 57 * 58 * <p>Here, <code><em>protocol</em></code> is the transport 59 * protocol to be used to connect to the connector server. It is 60 * a string of one or more ASCII characters, each of which is a 61 * letter, a digit, or one of the characters <code>+</code> or 62 * <code>-</code>. The first character must be a letter. 63 * Uppercase letters are converted into lowercase ones.</p> 64 * 65 * <p><code><em>sap</em></code> is the address at which the connector 66 * server is found. This address uses a subset of the syntax defined 67 * by RFC 2609 for IP-based protocols. It is a subset because the 68 * <code>user@host</code> syntax is not supported.</p> 69 * 70 * <p>The other syntaxes defined by RFC 2609 are not currently 71 * supported by this class.</p> 72 * 73 * <p>The supported syntax is:</p> 74 * 75 * <blockquote> 76 * 77 * <code>//<em>[host[</em>:<em>port]][url-path]</em></code> 78 * 79 * </blockquote> 80 * 81 * <p>Square brackets <code>[]</code> indicate optional parts of 82 * the address. Not all protocols will recognize all optional 83 * parts.</p> 84 * 85 * <p>The <code><em>host</em></code> is a host name, an IPv4 numeric 86 * host address, or an IPv6 numeric address enclosed in square 87 * brackets.</p> 88 * 89 * <p>The <code><em>port</em></code> is a decimal port number. 0 90 * means a default or anonymous port, depending on the protocol.</p> 91 * 92 * <p>The <code><em>host</em></code> and <code><em>port</em></code> 93 * can be omitted. The <code><em>port</em></code> cannot be supplied 94 * without a <code><em>host</em></code>.</p> 95 * 96 * <p>The <code><em>url-path</em></code>, if any, begins with a slash 97 * (<code>/</code>) or a semicolon (<code>;</code>) and continues to 98 * the end of the address. It can contain attributes using the 99 * semicolon syntax specified in RFC 2609. Those attributes are not 100 * parsed by this class and incorrect attribute syntax is not 101 * detected.</p> 102 * 103 * <p>Although it is legal according to RFC 2609 to have a 104 * <code><em>url-path</em></code> that begins with a semicolon, not 105 * all implementations of SLP allow it, so it is recommended to avoid 106 * that syntax.</p> 107 * 108 * <p>Case is not significant in the initial 109 * <code>service:jmx:<em>protocol</em></code> string or in the host 110 * part of the address. Depending on the protocol, case can be 111 * significant in the <code><em>url-path</em></code>.</p> 112 * 113 * @see <a 114 * href="http://www.ietf.org/rfc/rfc2609.txt">RFC 2609, 115 * "Service Templates and <code>Service:</code> Schemes"</a> 116 * @see <a 117 * href="http://www.ietf.org/rfc/rfc3111.txt">RFC 3111, 118 * "Service Location Protocol Modifications for IPv6"</a> 119 * 120 * @since 1.5 121 */ 122 public class JMXServiceURL implements Serializable { 123 124 private static final long serialVersionUID = 8173364409860779292L; 125 126 /** 127 * <p>Constructs a <code>JMXServiceURL</code> by parsing a Service URL 128 * string.</p> 129 * 130 * @param serviceURL the URL string to be parsed. 131 * 132 * @exception NullPointerException if <code>serviceURL</code> is 133 * null. 134 * 135 * @exception MalformedURLException if <code>serviceURL</code> 136 * does not conform to the syntax for an Abstract Service URL or 137 * if it is not a valid name for a JMX Remote API service. A 138 * <code>JMXServiceURL</code> must begin with the string 139 * <code>"service:jmx:"</code> (case-insensitive). It must not 140 * contain any characters that are not printable ASCII characters. 141 */ 142 public JMXServiceURL(String serviceURL) throws MalformedURLException { 143 final int serviceURLLength = serviceURL.length(); 144 145 /* Check that there are no non-ASCII characters in the URL, 146 following RFC 2609. */ 147 for (int i = 0; i < serviceURLLength; i++) { 148 char c = serviceURL.charAt(i); 149 if (c < 32 || c >= 127) { 150 throw new MalformedURLException("Service URL contains " + 151 "non-ASCII character 0x" + 152 Integer.toHexString(c)); 153 } 154 } 155 156 // Parse the required prefix 157 final String requiredPrefix = "service:jmx:"; 158 final int requiredPrefixLength = requiredPrefix.length(); 159 if (!serviceURL.regionMatches(true, // ignore case 160 0, // serviceURL offset 161 requiredPrefix, 162 0, // requiredPrefix offset 163 requiredPrefixLength)) { 164 throw new MalformedURLException("Service URL must start with " + 165 requiredPrefix); 166 } 167 168 // Parse the protocol name 169 final int protoStart = requiredPrefixLength; 170 final int protoEnd = indexOf(serviceURL, ':', protoStart); 171 this.protocol = 172 serviceURL.substring(protoStart, protoEnd).toLowerCase(Locale.ENGLISH); 173 174 if (!serviceURL.regionMatches(protoEnd, "://", 0, 3)) { 175 throw new MalformedURLException("Missing \"://\" after " + 176 "protocol name"); 177 } 178 179 // Parse the host name 180 final int hostStart = protoEnd + 3; 181 final int hostEnd; 182 if (hostStart < serviceURLLength 183 && serviceURL.charAt(hostStart) == '[') { 184 hostEnd = serviceURL.indexOf(']', hostStart) + 1; 185 if (hostEnd == 0) 186 throw new MalformedURLException("Bad host name: [ without ]"); 187 this.host = serviceURL.substring(hostStart + 1, hostEnd - 1); 188 if (!isNumericIPv6Address(this.host)) { 189 throw new MalformedURLException("Address inside [...] must " + 190 "be numeric IPv6 address"); 191 } 192 } else { 193 hostEnd = 194 indexOfFirstNotInSet(serviceURL, hostNameBitSet, hostStart); 195 this.host = serviceURL.substring(hostStart, hostEnd); 196 } 197 198 // Parse the port number 199 final int portEnd; 200 if (hostEnd < serviceURLLength && serviceURL.charAt(hostEnd) == ':') { 201 if (this.host.length() == 0) { 202 throw new MalformedURLException("Cannot give port number " + 203 "without host name"); 204 } 205 final int portStart = hostEnd + 1; 206 portEnd = 207 indexOfFirstNotInSet(serviceURL, numericBitSet, portStart); 208 final String portString = serviceURL.substring(portStart, portEnd); 209 try { 210 this.port = Integer.parseInt(portString); 211 } catch (NumberFormatException e) { 212 throw new MalformedURLException("Bad port number: \"" + 213 portString + "\": " + e); 214 } 215 } else { 216 portEnd = hostEnd; 217 this.port = 0; 218 } 219 220 // Parse the URL path 221 final int urlPathStart = portEnd; 222 if (urlPathStart < serviceURLLength) 223 this.urlPath = serviceURL.substring(urlPathStart); 224 else 225 this.urlPath = ""; 226 227 validate(); 228 } 229 230 /** 231 * <p>Constructs a <code>JMXServiceURL</code> with the given protocol, 232 * host, and port. This constructor is equivalent to 233 * {@link #JMXServiceURL(String, String, int, String) 234 * JMXServiceURL(protocol, host, port, null)}.</p> 235 * 236 * @param protocol the protocol part of the URL. If null, defaults 237 * to <code>jmxmp</code>. 238 * 239 * @param host the host part of the URL. If null, defaults to the 240 * local host name, as determined by 241 * <code>InetAddress.getLocalHost().getHostName()</code>. If it 242 * is a numeric IPv6 address, it can optionally be enclosed in 243 * square brackets <code>[]</code>. 244 * 245 * @param port the port part of the URL. 246 * 247 * @exception MalformedURLException if one of the parts is 248 * syntactically incorrect, or if <code>host</code> is null and it 249 * is not possible to find the local host name, or if 250 * <code>port</code> is negative. 251 */ 252 public JMXServiceURL(String protocol, String host, int port) 253 throws MalformedURLException { 254 this(protocol, host, port, null); 255 } 256 257 /** 258 * <p>Constructs a <code>JMXServiceURL</code> with the given parts. 259 * 260 * @param protocol the protocol part of the URL. If null, defaults 261 * to <code>jmxmp</code>. 262 * 263 * @param host the host part of the URL. If null, defaults to the 264 * local host name, as determined by 265 * <code>InetAddress.getLocalHost().getHostName()</code>. If it 266 * is a numeric IPv6 address, it can optionally be enclosed in 267 * square brackets <code>[]</code>. 268 * 269 * @param port the port part of the URL. 270 * 271 * @param urlPath the URL path part of the URL. If null, defaults to 272 * the empty string. 273 * 274 * @exception MalformedURLException if one of the parts is 275 * syntactically incorrect, or if <code>host</code> is null and it 276 * is not possible to find the local host name, or if 277 * <code>port</code> is negative. 278 */ 279 public JMXServiceURL(String protocol, String host, int port, 280 String urlPath) 281 throws MalformedURLException { 282 if (protocol == null) 283 protocol = "jmxmp"; 284 285 if (host == null) { 286 InetAddress local; 287 try { 288 local = InetAddress.getLocalHost(); 289 } catch (UnknownHostException e) { 290 throw new MalformedURLException("Local host name unknown: " + 291 e); 292 } 293 294 host = local.getHostName(); 295 296 /* We might have a hostname that violates DNS naming 297 rules, for example that contains an `_'. While we 298 could be strict and throw an exception, this is rather 299 user-hostile. Instead we use its numerical IP address. 300 We can only reasonably do this for the host==null case. 301 If we're given an explicit host name that is illegal we 302 have to reject it. (Bug 5057532.) */ 303 try { 304 validateHost(host, port); 305 } catch (MalformedURLException e) { 306 if (logger.fineOn()) { 307 logger.fine("JMXServiceURL", 308 "Replacing illegal local host name " + 309 host + " with numeric IP address " + 310 "(see RFC 1034)", e); 311 } 312 host = local.getHostAddress(); 313 /* Use the numeric address, which could be either IPv4 314 or IPv6. validateHost will accept either. */ 315 } 316 } 317 318 if (host.startsWith("[")) { 319 if (!host.endsWith("]")) { 320 throw new MalformedURLException("Host starts with [ but " + 321 "does not end with ]"); 322 } 323 host = host.substring(1, host.length() - 1); 324 if (!isNumericIPv6Address(host)) { 325 throw new MalformedURLException("Address inside [...] must " + 326 "be numeric IPv6 address"); 327 } 328 if (host.startsWith("[")) 329 throw new MalformedURLException("More than one [[...]]"); 330 } 331 332 this.protocol = protocol.toLowerCase(Locale.ENGLISH); 333 this.host = host; 334 this.port = port; 335 336 if (urlPath == null) 337 urlPath = ""; 338 this.urlPath = urlPath; 339 340 validate(); 341 } 342 343 private static final String INVALID_INSTANCE_MSG = 344 "Trying to deserialize an invalid instance of JMXServiceURL"; 345 private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { 346 ObjectInputStream.GetField gf = inputStream.readFields(); 347 String h = (String)gf.get("host", null); 348 int p = gf.get("port", -1); 349 String proto = (String)gf.get("protocol", null); 350 String url = (String)gf.get("urlPath", null); 351 352 if (proto == null || url == null || h == null) { 353 StringBuilder sb = new StringBuilder(INVALID_INSTANCE_MSG).append('['); 354 boolean empty = true; 355 if (proto == null) { 356 sb.append("protocol=null"); 357 empty = false; 358 } 359 if (h == null) { 360 sb.append(empty ? "" : ",").append("host=null"); 361 empty = false; 362 } 363 if (url == null) { 364 sb.append(empty ? "" : ",").append("urlPath=null"); 365 } 366 sb.append(']'); 367 throw new InvalidObjectException(sb.toString()); 368 } 369 370 if (h.contains("[") || h.contains("]")) { 371 throw new InvalidObjectException("Invalid host name: " + h); 372 } 373 374 try { 375 validate(proto, h, p, url); 376 this.protocol = proto; 377 this.host = h; 378 this.port = p; 379 this.urlPath = url; 380 } catch (MalformedURLException e) { 381 throw new InvalidObjectException(INVALID_INSTANCE_MSG + ": " + 382 e.getMessage()); 383 } 384 385 } 386 387 private void validate(String proto, String h, int p, String url) 388 throws MalformedURLException { 389 // Check protocol 390 final int protoEnd = indexOfFirstNotInSet(proto, protocolBitSet, 0); 391 if (protoEnd == 0 || protoEnd < proto.length() 392 || !alphaBitSet.get(proto.charAt(0))) { 393 throw new MalformedURLException("Missing or invalid protocol " + 394 "name: \"" + proto + "\""); 395 } 396 397 // Check host 398 validateHost(h, p); 399 400 // Check port 401 if (p < 0) 402 throw new MalformedURLException("Bad port: " + p); 403 404 // Check URL path 405 if (url.length() > 0) { 406 if (!url.startsWith("/") && !url.startsWith(";")) 407 throw new MalformedURLException("Bad URL path: " + url); 408 } 409 } 410 411 private void validate() throws MalformedURLException { 412 validate(this.protocol, this.host, this.port, this.urlPath); 413 } 414 415 private static void validateHost(String h, int port) 416 throws MalformedURLException { 417 418 if (h.length() == 0) { 419 if (port != 0) { 420 throw new MalformedURLException("Cannot give port number " + 421 "without host name"); 422 } 423 return; 424 } 425 426 if (isNumericIPv6Address(h)) { 427 /* We assume J2SE >= 1.4 here. Otherwise you can't 428 use the address anyway. We can't call 429 InetAddress.getByName without checking for a 430 numeric IPv6 address, because we mustn't try to do 431 a DNS lookup in case the address is not actually 432 numeric. */ 433 try { 434 InetAddress.getByName(h); 435 } catch (Exception e) { 436 /* We should really catch UnknownHostException 437 here, but a bug in JDK 1.4 causes it to throw 438 ArrayIndexOutOfBoundsException, e.g. if the 439 string is ":". */ 440 MalformedURLException bad = 441 new MalformedURLException("Bad IPv6 address: " + h); 442 EnvHelp.initCause(bad, e); 443 throw bad; 444 } 445 } else { 446 /* Tiny state machine to check valid host name. This 447 checks the hostname grammar from RFC 1034 (DNS), 448 page 11. A hostname is a dot-separated list of one 449 or more labels, where each label consists of 450 letters, numbers, or hyphens. A label cannot begin 451 or end with a hyphen. Empty hostnames are not 452 allowed. Note that numeric IPv4 addresses are a 453 special case of this grammar. 454 455 The state is entirely captured by the last 456 character seen, with a virtual `.' preceding the 457 name. We represent any alphanumeric character by 458 `a'. 459 460 We need a special hack to check, as required by the 461 RFC 2609 (SLP) grammar, that the last component of 462 the hostname begins with a letter. Respecting the 463 intent of the RFC, we only do this if there is more 464 than one component. If your local hostname begins 465 with a digit, we don't reject it. */ 466 final int hostLen = h.length(); 467 char lastc = '.'; 468 boolean sawDot = false; 469 char componentStart = 0; 470 471 loop: 472 for (int i = 0; i < hostLen; i++) { 473 char c = h.charAt(i); 474 boolean isAlphaNumeric = alphaNumericBitSet.get(c); 475 if (lastc == '.') 476 componentStart = c; 477 if (isAlphaNumeric) 478 lastc = 'a'; 479 else if (c == '-') { 480 if (lastc == '.') 481 break; // will throw exception 482 lastc = '-'; 483 } else if (c == '.') { 484 sawDot = true; 485 if (lastc != 'a') 486 break; // will throw exception 487 lastc = '.'; 488 } else { 489 lastc = '.'; // will throw exception 490 break; 491 } 492 } 493 494 try { 495 if (lastc != 'a') 496 throw randomException; 497 if (sawDot && !alphaBitSet.get(componentStart)) { 498 /* Must be a numeric IPv4 address. In addition to 499 the explicitly-thrown exceptions, we can get 500 NoSuchElementException from the calls to 501 tok.nextToken and NumberFormatException from 502 the call to Integer.parseInt. Using exceptions 503 for control flow this way is a bit evil but it 504 does simplify things enormously. */ 505 StringTokenizer tok = new StringTokenizer(h, ".", true); 506 for (int i = 0; i < 4; i++) { 507 String ns = tok.nextToken(); 508 int n = Integer.parseInt(ns); 509 if (n < 0 || n > 255) 510 throw randomException; 511 if (i < 3 && !tok.nextToken().equals(".")) 512 throw randomException; 513 } 514 if (tok.hasMoreTokens()) 515 throw randomException; 516 } 517 } catch (Exception e) { 518 throw new MalformedURLException("Bad host: \"" + h + "\""); 519 } 520 } 521 } 522 523 private static final Exception randomException = new Exception(); 524 525 526 /** 527 * <p>The protocol part of the Service URL. 528 * 529 * @return the protocol part of the Service URL. This is never null. 530 */ 531 public String getProtocol() { 532 return protocol; 533 } 534 535 /** 536 * <p>The host part of the Service URL. If the Service URL was 537 * constructed with the constructor that takes a URL string 538 * parameter, the result is the substring specifying the host in 539 * that URL. If the Service URL was constructed with a 540 * constructor that takes a separate host parameter, the result is 541 * the string that was specified. If that string was null, the 542 * result is 543 * <code>InetAddress.getLocalHost().getHostName()</code>.</p> 544 * 545 * <p>In either case, if the host was specified using the 546 * <code>[...]</code> syntax for numeric IPv6 addresses, the 547 * square brackets are not included in the return value here.</p> 548 * 549 * @return the host part of the Service URL. This is never null. 550 */ 551 public String getHost() { 552 return host; 553 } 554 555 /** 556 * <p>The port of the Service URL. If no port was 557 * specified, the returned value is 0.</p> 558 * 559 * @return the port of the Service URL, or 0 if none. 560 */ 561 public int getPort() { 562 return port; 563 } 564 565 /** 566 * <p>The URL Path part of the Service URL. This is an empty 567 * string, or a string beginning with a slash (<code>/</code>), or 568 * a string beginning with a semicolon (<code>;</code>). 569 * 570 * @return the URL Path part of the Service URL. This is never 571 * null. 572 */ 573 public String getURLPath() { 574 return urlPath; 575 } 576 577 /** 578 * <p>The string representation of this Service URL. If the value 579 * returned by this method is supplied to the 580 * <code>JMXServiceURL</code> constructor, the resultant object is 581 * equal to this one.</p> 582 * 583 * <p>The <code><em>host</em></code> part of the returned string 584 * is the value returned by {@link #getHost()}. If that value 585 * specifies a numeric IPv6 address, it is surrounded by square 586 * brackets <code>[]</code>.</p> 587 * 588 * <p>The <code><em>port</em></code> part of the returned string 589 * is the value returned by {@link #getPort()} in its shortest 590 * decimal form. If the value is zero, it is omitted.</p> 591 * 592 * @return the string representation of this Service URL. 593 */ 594 public String toString() { 595 /* We don't bother synchronizing the access to toString. At worst, 596 n threads will independently compute and store the same value. */ 597 if (toString != null) 598 return toString; 599 StringBuilder buf = new StringBuilder("service:jmx:"); 600 buf.append(getProtocol()).append("://"); 601 final String getHost = getHost(); 602 if (isNumericIPv6Address(getHost)) 603 buf.append('[').append(getHost).append(']'); 604 else 605 buf.append(getHost); 606 final int getPort = getPort(); 607 if (getPort != 0) 608 buf.append(':').append(getPort); 609 buf.append(getURLPath()); 610 toString = buf.toString(); 611 return toString; 612 } 613 614 /** 615 * <p>Indicates whether some other object is equal to this one. 616 * This method returns true if and only if <code>obj</code> is an 617 * instance of <code>JMXServiceURL</code> whose {@link 618 * #getProtocol()}, {@link #getHost()}, {@link #getPort()}, and 619 * {@link #getURLPath()} methods return the same values as for 620 * this object. The values for {@link #getProtocol()} and {@link 621 * #getHost()} can differ in case without affecting equality. 622 * 623 * @param obj the reference object with which to compare. 624 * 625 * @return <code>true</code> if this object is the same as the 626 * <code>obj</code> argument; <code>false</code> otherwise. 627 */ 628 public boolean equals(Object obj) { 629 if (!(obj instanceof JMXServiceURL)) 630 return false; 631 JMXServiceURL u = (JMXServiceURL) obj; 632 return 633 (u.getProtocol().equalsIgnoreCase(getProtocol()) && 634 u.getHost().equalsIgnoreCase(getHost()) && 635 u.getPort() == getPort() && 636 u.getURLPath().equals(getURLPath())); 637 } 638 639 public int hashCode() { 640 return toString().hashCode(); 641 } 642 643 /* True if this string, assumed to be a valid argument to 644 * InetAddress.getByName, is a numeric IPv6 address. 645 */ 646 private static boolean isNumericIPv6Address(String s) { 647 // address contains colon if and only if it's a numeric IPv6 address 648 return (s.indexOf(':') >= 0); 649 } 650 651 // like String.indexOf but returns string length not -1 if not present 652 private static int indexOf(String s, char c, int fromIndex) { 653 int index = s.indexOf(c, fromIndex); 654 if (index < 0) 655 return s.length(); 656 else 657 return index; 658 } 659 660 private static int indexOfFirstNotInSet(String s, BitSet set, 661 int fromIndex) { 662 final int slen = s.length(); 663 int i = fromIndex; 664 while (true) { 665 if (i >= slen) 666 break; 667 char c = s.charAt(i); 668 if (c >= 128) 669 break; // not ASCII 670 if (!set.get(c)) 671 break; 672 i++; 673 } 674 return i; 675 } 676 677 private final static BitSet alphaBitSet = new BitSet(128); 678 private final static BitSet numericBitSet = new BitSet(128); 679 private final static BitSet alphaNumericBitSet = new BitSet(128); 680 private final static BitSet protocolBitSet = new BitSet(128); 681 private final static BitSet hostNameBitSet = new BitSet(128); 682 static { 683 /* J2SE 1.4 adds lots of handy methods to BitSet that would 684 allow us to simplify here, e.g. by not writing loops, but 685 we want to work on J2SE 1.3 too. */ 686 687 for (char c = '0'; c <= '9'; c++) 688 numericBitSet.set(c); 689 690 for (char c = 'A'; c <= 'Z'; c++) 691 alphaBitSet.set(c); 692 for (char c = 'a'; c <= 'z'; c++) 693 alphaBitSet.set(c); 694 695 alphaNumericBitSet.or(alphaBitSet); 696 alphaNumericBitSet.or(numericBitSet); 697 698 protocolBitSet.or(alphaNumericBitSet); 699 protocolBitSet.set('+'); 700 protocolBitSet.set('-'); 701 702 hostNameBitSet.or(alphaNumericBitSet); 703 hostNameBitSet.set('-'); 704 hostNameBitSet.set('.'); 705 } 706 707 /** 708 * The value returned by {@link #getProtocol()}. 709 */ 710 private String protocol; 711 712 /** 713 * The value returned by {@link #getHost()}. 714 */ 715 private String host; 716 717 /** 718 * The value returned by {@link #getPort()}. 719 */ 720 private int port; 721 722 /** 723 * The value returned by {@link #getURLPath()}. 724 */ 725 private String urlPath; 726 727 /** 728 * Cached result of {@link #toString()}. 729 */ 730 private transient String toString; 731 732 private static final ClassLogger logger = 733 new ClassLogger("javax.management.remote.misc", "JMXServiceURL"); 734 }