JMXServiceURLpublic class JMXServiceURL extends Object implements SerializableThe address of a JMX API connector server. Instances of this class
are immutable.
The address is an Abstract Service URL for SLP, as
defined in RFC 2609 and amended by RFC 3111. It must look like
this:
service:jmx:protocol:sap
Here, protocol is the transport
protocol to be used to connect to the connector server. It is
a string of one or more ASCII characters, each of which is a
letter, a digit, or one of the characters + or
- . The first character must be a letter.
Uppercase letters are converted into lowercase ones.
sap is the address at which the connector
server is found. This address uses a subset of the syntax defined
by RFC 2609 for IP-based protocols. It is a subset because the
user@host syntax is not supported.
The other syntaxes defined by RFC 2609 are not currently
supported by this class.
The supported syntax is:
//[host[:port]][url-path]
Square brackets [] indicate optional parts of
the address. Not all protocols will recognize all optional
parts.
The host is a host name, an IPv4 numeric
host address, or an IPv6 numeric address enclosed in square
brackets.
The port is a decimal port number. 0
means a default or anonymous port, depending on the protocol.
The host and port
can be omitted. The port cannot be supplied
without a host .
The url-path , if any, begins with a slash
(/ ) or a semicolon (; ) and continues to
the end of the address. It can contain attributes using the
semicolon syntax specified in RFC 2609. Those attributes are not
parsed by this class and incorrect attribute syntax is not
detected.
Although it is legal according to RFC 2609 to have a
url-path that begins with a semicolon, not
all implementations of SLP allow it, so it is recommended to avoid
that syntax.
Case is not significant in the initial
service:jmx:protocol string or in the host
part of the address. Depending on the protocol, case can be
significant in the url-path . |
Fields Summary |
---|
private static final long | serialVersionUID | private static final Exception | randomException | private static final BitSet | alphaBitSet | private static final BitSet | numericBitSet | private static final BitSet | alphaNumericBitSet | private static final BitSet | protocolBitSet | private static final BitSet | hostNameBitSet | private final String | protocolThe value returned by {@link #getProtocol()}. | private final String | hostThe value returned by {@link #getHost()}. | private final int | portThe value returned by {@link #getPort()}. | private final String | urlPathThe value returned by {@link #getURLPath()}. | private transient String | toStringCached result of {@link #toString()}. | private static final ClassLogger | logger |
Constructors Summary |
---|
public JMXServiceURL(String serviceURL)Constructs a JMXServiceURL by parsing a Service URL
string.
final int serviceURLLength = serviceURL.length();
/* Check that there are no non-ASCII characters in the URL,
following RFC 2609. */
for (int i = 0; i < serviceURLLength; i++) {
char c = serviceURL.charAt(i);
if (c < 32 || c >= 127) {
throw new MalformedURLException("Service URL contains " +
"non-ASCII character 0x" +
Integer.toHexString(c));
}
}
// Parse the required prefix
final String requiredPrefix = "service:jmx:";
final int requiredPrefixLength = requiredPrefix.length();
if (!serviceURL.regionMatches(true, // ignore case
0, // serviceURL offset
requiredPrefix,
0, // requiredPrefix offset
requiredPrefixLength)) {
throw new MalformedURLException("Service URL must start with " +
requiredPrefix);
}
int[] ptr = new int[1];
// Parse the protocol name
final int protoStart = requiredPrefixLength;
final int protoEnd = indexOf(serviceURL, ':", protoStart);
this.protocol =
serviceURL.substring(protoStart, protoEnd).toLowerCase();
if (!serviceURL.regionMatches(protoEnd, "://", 0, 3)) {
throw new MalformedURLException("Missing \"://\" after " +
"protocol name");
}
// Parse the host name
final int hostStart = protoEnd + 3;
final int hostEnd;
if (hostStart < serviceURLLength
&& serviceURL.charAt(hostStart) == '[") {
hostEnd = serviceURL.indexOf(']", hostStart) + 1;
if (hostEnd == 0)
throw new MalformedURLException("Bad host name: [ without ]");
this.host = serviceURL.substring(hostStart + 1, hostEnd - 1);
if (!isNumericIPv6Address(this.host)) {
throw new MalformedURLException("Address inside [...] must " +
"be numeric IPv6 address");
}
} else {
hostEnd =
indexOfFirstNotInSet(serviceURL, hostNameBitSet, hostStart);
this.host = serviceURL.substring(hostStart, hostEnd);
}
// Parse the port number
final int portEnd;
if (hostEnd < serviceURLLength && serviceURL.charAt(hostEnd) == ':") {
if (this.host.length() == 0) {
throw new MalformedURLException("Cannot give port number " +
"without host name");
}
final int portStart = hostEnd + 1;
portEnd =
indexOfFirstNotInSet(serviceURL, numericBitSet, portStart);
final String portString = serviceURL.substring(portStart, portEnd);
try {
this.port = Integer.parseInt(portString);
} catch (NumberFormatException e) {
throw new MalformedURLException("Bad port number: \"" +
portString + "\": " + e);
}
} else {
portEnd = hostEnd;
this.port = 0;
}
// Parse the URL path
final int urlPathStart = portEnd;
if (urlPathStart < serviceURLLength)
this.urlPath = serviceURL.substring(urlPathStart);
else
this.urlPath = "";
validate();
| public JMXServiceURL(String protocol, String host, int port)Constructs a JMXServiceURL with the given protocol,
host, and port. This constructor is equivalent to
{@link #JMXServiceURL(String, String, int, String)
JMXServiceURL(protocol, host, port, null)}.
this(protocol, host, port, null);
| public JMXServiceURL(String protocol, String host, int port, String urlPath)Constructs a JMXServiceURL with the given parts.
if (protocol == null)
protocol = "jmxmp";
if (host == null) {
InetAddress local;
try {
local = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
throw new MalformedURLException("Local host name unknown: " +
e);
}
host = local.getHostName();
/* We might have a hostname that violates DNS naming
rules, for example that contains an `_'. While we
could be strict and throw an exception, this is rather
user-hostile. Instead we use its numerical IP address.
We can only reasonably do this for the host==null case.
If we're given an explicit host name that is illegal we
have to reject it. (Bug 5057532.) */
try {
validateHost(host);
} catch (MalformedURLException e) {
if (logger.fineOn()) {
logger.fine("JMXServiceURL",
"Replacing illegal local host name " +
host + " with numeric IP address " +
"(see RFC 1034)", e);
}
host = local.getHostAddress();
/* Use the numeric address, which could be either IPv4
or IPv6. validateHost will accept either. */
}
}
if (host.startsWith("[")) {
if (!host.endsWith("]")) {
throw new MalformedURLException("Host starts with [ but " +
"does not end with ]");
}
host = host.substring(1, host.length() - 1);
if (!isNumericIPv6Address(host)) {
throw new MalformedURLException("Address inside [...] must " +
"be numeric IPv6 address");
}
if (host.startsWith("["))
throw new MalformedURLException("More than one [[...]]");
}
this.protocol = protocol.toLowerCase();
this.host = host;
this.port = port;
if (urlPath == null)
urlPath = "";
this.urlPath = urlPath;
validate();
|
Methods Summary |
---|
private static void | addCharsToBitSet(java.util.BitSet set, java.lang.String chars)
/* J2SE 1.4 adds lots of handy methods to BitSet that would
allow us to simplify here, e.g. by not writing loops, but
we want to work on J2SE 1.3 too. */
for (char c = '0"; c <= '9"; c++)
numericBitSet.set(c);
for (char c = 'A"; c <= 'Z"; c++)
alphaBitSet.set(c);
for (char c = 'a"; c <= 'z"; c++)
alphaBitSet.set(c);
alphaNumericBitSet.or(alphaBitSet);
alphaNumericBitSet.or(numericBitSet);
protocolBitSet.or(alphaNumericBitSet);
protocolBitSet.set('+");
protocolBitSet.set('-");
hostNameBitSet.or(alphaNumericBitSet);
hostNameBitSet.set('-");
hostNameBitSet.set('.");
for (int i = 0; i < chars.length(); i++)
set.set(chars.charAt(i));
| public boolean | equals(java.lang.Object obj)Indicates whether some other object is equal to this one.
This method returns true if and only if obj is an
instance of JMXServiceURL whose {@link
#getProtocol()}, {@link #getHost()}, {@link #getPort()}, and
{@link #getURLPath()} methods return the same values as for
this object. The values for {@link #getProtocol()} and {@link
#getHost()} can differ in case without affecting equality.
if (!(obj instanceof JMXServiceURL))
return false;
JMXServiceURL u = (JMXServiceURL) obj;
return
(u.getProtocol().equalsIgnoreCase(getProtocol()) &&
u.getHost().equalsIgnoreCase(getHost()) &&
u.getPort() == getPort() &&
u.getURLPath().equals(getURLPath()));
| public java.lang.String | getHost()The host part of the Service URL. If the Service URL was
constructed with the constructor that takes a URL string
parameter, the result is the substring specifying the host in
that URL. If the Service URL was constructed with a
constructor that takes a separate host parameter, the result is
the string that was specified. If that string was null, the
result is
InetAddress.getLocalHost().getHostName() .
In either case, if the host was specified using the
[...] syntax for numeric IPv6 addresses, the
square brackets are not included in the return value here.
return host;
| public int | getPort()The port of the Service URL. If no port was
specified, the returned value is 0.
return port;
| public java.lang.String | getProtocol()The protocol part of the Service URL.
return protocol;
| public java.lang.String | getURLPath()The URL Path part of the Service URL. This is an empty
string, or a string beginning with a slash (/ ), or
a string beginning with a semicolon (; ).
return urlPath;
| public int | hashCode()
return toString().hashCode();
| private static int | indexOf(java.lang.String s, char c, int fromIndex)
int index = s.indexOf(c, fromIndex);
if (index < 0)
return s.length();
else
return index;
| private static int | indexOfFirstNotInSet(java.lang.String s, java.util.BitSet set, int fromIndex)
final int slen = s.length();
int i = fromIndex;
while (true) {
if (i >= slen)
break;
char c = s.charAt(i);
if (c >= 128)
break; // not ASCII
if (!set.get(c))
break;
i++;
}
return i;
| private static boolean | isNumericIPv6Address(java.lang.String s)
// address contains colon if and only if it's a numeric IPv6 address
return (s.indexOf(':") >= 0);
| public java.lang.String | toString()The string representation of this Service URL. If the value
returned by this method is supplied to the
JMXServiceURL constructor, the resultant object is
equal to this one.
The host part of the returned string
is the value returned by {@link #getHost()}. If that value
specifies a numeric IPv6 address, it is surrounded by square
brackets [] .
The port part of the returned string
is the value returned by {@link #getPort()} in its shortest
decimal form. If the value is zero, it is omitted.
/* We don't bother synchronizing the access to toString. At worst,
n threads will independently compute and store the same value. */
if (toString != null)
return toString;
StringBuffer buf = new StringBuffer("service:jmx:");
buf.append(getProtocol()).append("://");
final String getHost = getHost();
if (isNumericIPv6Address(getHost))
buf.append('[").append(getHost).append(']");
else
buf.append(getHost);
final int getPort = getPort();
if (getPort != 0)
buf.append(':").append(getPort);
buf.append(getURLPath());
toString = buf.toString();
return toString;
| private void | validate()
// Check protocol
final int protoEnd = indexOfFirstNotInSet(protocol, protocolBitSet, 0);
if (protoEnd == 0 || protoEnd < protocol.length()
|| !alphaBitSet.get(protocol.charAt(0))) {
throw new MalformedURLException("Missing or invalid protocol " +
"name: \"" + protocol + "\"");
}
// Check host
validateHost();
// Check port
if (port < 0)
throw new MalformedURLException("Bad port: " + port);
// Check URL path
if (urlPath.length() > 0) {
if (!urlPath.startsWith("/") && !urlPath.startsWith(";"))
throw new MalformedURLException("Bad URL path: " + urlPath);
}
| private void | validateHost()
if (host.length() == 0) {
if (port != 0) {
throw new MalformedURLException("Cannot give port number " +
"without host name");
}
return;
}
validateHost(host);
| private static void | validateHost(java.lang.String h)
if (isNumericIPv6Address(h)) {
/* We assume J2SE >= 1.4 here. Otherwise you can't
use the address anyway. We can't call
InetAddress.getByName without checking for a
numeric IPv6 address, because we mustn't try to do
a DNS lookup in case the address is not actually
numeric. */
try {
InetAddress.getByName(h);
} catch (Exception e) {
/* We should really catch UnknownHostException
here, but a bug in JDK 1.4 causes it to throw
ArrayIndexOutOfBoundsException, e.g. if the
string is ":". */
MalformedURLException bad =
new MalformedURLException("Bad IPv6 address: " + h);
EnvHelp.initCause(bad, e);
throw bad;
}
} else {
/* Tiny state machine to check valid host name. This
checks the hostname grammar from RFC 1034 (DNS),
page 11. A hostname is a dot-separated list of one
or more labels, where each label consists of
letters, numbers, or hyphens. A label cannot begin
or end with a hyphen. Empty hostnames are not
allowed. Note that numeric IPv4 addresses are a
special case of this grammar.
The state is entirely captured by the last
character seen, with a virtual `.' preceding the
name. We represent any alphanumeric character by
`a'.
We need a special hack to check, as required by the
RFC 2609 (SLP) grammar, that the last component of
the hostname begins with a letter. Respecting the
intent of the RFC, we only do this if there is more
than one component. If your local hostname begins
with a digit, we don't reject it. */
final int hostLen = h.length();
char lastc = '.";
boolean sawDot = false;
char componentStart = 0;
loop:
for (int i = 0; i < hostLen; i++) {
char c = h.charAt(i);
boolean isAlphaNumeric = alphaNumericBitSet.get(c);
if (lastc == '.")
componentStart = c;
if (isAlphaNumeric)
lastc = 'a";
else if (c == '-") {
if (lastc == '.")
break; // will throw exception
lastc = '-";
} else if (c == '.") {
sawDot = true;
if (lastc != 'a")
break; // will throw exception
lastc = '.";
} else {
lastc = '."; // will throw exception
break;
}
}
try {
if (lastc != 'a")
throw randomException;
if (sawDot && !alphaBitSet.get(componentStart)) {
/* Must be a numeric IPv4 address. In addition to
the explicitly-thrown exceptions, we can get
NoSuchElementException from the calls to
tok.nextToken and NumberFormatException from
the call to Integer.parseInt. Using exceptions
for control flow this way is a bit evil but it
does simplify things enormously. */
StringTokenizer tok = new StringTokenizer(h, ".", true);
for (int i = 0; i < 4; i++) {
String ns = tok.nextToken();
int n = Integer.parseInt(ns);
if (n < 0 || n > 255)
throw randomException;
if (i < 3 && !tok.nextToken().equals("."))
throw randomException;
}
if (tok.hasMoreTokens())
throw randomException;
}
} catch (Exception e) {
throw new MalformedURLException("Bad host: \"" + h + "\"");
}
}
|
|