/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* <p>The address of a JMX API connector server. Instances of this class
* are immutable.</p>
*
* <p>The address is an <em>Abstract Service URL</em> for SLP, as
* defined in RFC 2609 and amended by RFC 3111. It must look like
* this:</p>
*
* <blockquote>
*
* <code>service:jmx:<em>protocol</em>:<em>sap</em></code>
*
* </blockquote>
*
* <p>Here, <code><em>protocol</em></code> 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 <code>+</code> or
* <code>-</code>. The first character must be a letter.
* Uppercase letters are converted into lowercase ones.</p>
*
* <p><code><em>sap</em></code> 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
* <code>user@host</code> syntax is not supported.</p>
*
* <p>The other syntaxes defined by RFC 2609 are not currently
* supported by this class.</p>
*
* <p>The supported syntax is:</p>
*
* <blockquote>
*
* <code>//<em>[host[</em>:<em>port]][url-path]</em></code>
*
* </blockquote>
*
* <p>Square brackets <code>[]</code> indicate optional parts of
* the address. Not all protocols will recognize all optional
* parts.</p>
*
* <p>The <code><em>host</em></code> is a host name, an IPv4 numeric
* host address, or an IPv6 numeric address enclosed in square
* brackets.</p>
*
* <p>The <code><em>port</em></code> is a decimal port number. 0
* means a default or anonymous port, depending on the protocol.</p>
*
* <p>The <code><em>host</em></code> and <code><em>port</em></code>
* can be omitted. The <code><em>port</em></code> cannot be supplied
* without a <code><em>host</em></code>.</p>
*
* <p>The <code><em>url-path</em></code>, if any, begins with a slash
* (<code>/</code>) or a semicolon (<code>;</code>) 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.</p>
*
* <p>Although it is legal according to RFC 2609 to have a
* <code><em>url-path</em></code> that begins with a semicolon, not
* all implementations of SLP allow it, so it is recommended to avoid
* that syntax.</p>
*
* <p>Case is not significant in the initial
* <code>service:jmx:<em>protocol</em></code> string or in the host
* part of the address. Depending on the protocol, case can be
* significant in the <code><em>url-path</em></code>.</p>
*
* @see <a
* href="http://www.ietf.org/rfc/rfc2609.txt">RFC 2609,
* "Service Templates and <code>Service:</code> Schemes"</a>
* @see <a
* href="http://www.ietf.org/rfc/rfc3111.txt">RFC 3111,
* "Service Location Protocol Modifications for IPv6"</a>
*
* @since 1.5
*/
/**
* <p>Constructs a <code>JMXServiceURL</code> by parsing a Service URL
* string.</p>
*
* @param serviceURL the URL string to be parsed.
*
* @exception NullPointerException if <code>serviceURL</code> is
* null.
*
* @exception MalformedURLException if <code>serviceURL</code>
* does not conform to the syntax for an Abstract Service URL or
* if it is not a valid name for a JMX Remote API service. A
* <code>JMXServiceURL</code> must begin with the string
* <code>"service:jmx:"</code> (case-insensitive). It must not
* contain any characters that are not printable ASCII characters.
*/
/* 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
0, // serviceURL offset
0, // requiredPrefix offset
throw new MalformedURLException("Service URL must start with " +
}
// Parse the protocol name
final int protoStart = requiredPrefixLength;
this.protocol =
throw new MalformedURLException("Missing \"://\" after " +
"protocol name");
}
// Parse the host name
final int hostEnd;
if (hostStart < serviceURLLength
if (hostEnd == 0)
throw new MalformedURLException("Bad host name: [ without ]");
if (!isNumericIPv6Address(this.host)) {
throw new MalformedURLException("Address inside [...] must " +
"be numeric IPv6 address");
}
} else {
hostEnd =
}
// Parse the port number
final int portEnd;
throw new MalformedURLException("Cannot give port number " +
"without host name");
}
portEnd =
try {
} catch (NumberFormatException e) {
throw new MalformedURLException("Bad port number: \"" +
portString + "\": " + e);
}
} else {
this.port = 0;
}
// Parse the URL path
final int urlPathStart = portEnd;
if (urlPathStart < serviceURLLength)
else
this.urlPath = "";
validate();
}
/**
* <p>Constructs a <code>JMXServiceURL</code> with the given protocol,
* host, and port. This constructor is equivalent to
* {@link #JMXServiceURL(String, String, int, String)
* JMXServiceURL(protocol, host, port, null)}.</p>
*
* @param protocol the protocol part of the URL. If null, defaults
* to <code>jmxmp</code>.
*
* @param host the host part of the URL. If null, defaults to the
* local host name, as determined by
* <code>InetAddress.getLocalHost().getHostName()</code>. If it
* is a numeric IPv6 address, it can optionally be enclosed in
* square brackets <code>[]</code>.
*
* @param port the port part of the URL.
*
* @exception MalformedURLException if one of the parts is
* syntactically incorrect, or if <code>host</code> is null and it
* is not possible to find the local host name, or if
* <code>port</code> is negative.
*/
throws MalformedURLException {
}
/**
* <p>Constructs a <code>JMXServiceURL</code> with the given parts.
*
* @param protocol the protocol part of the URL. If null, defaults
* to <code>jmxmp</code>.
*
* @param host the host part of the URL. If null, defaults to the
* local host name, as determined by
* <code>InetAddress.getLocalHost().getHostName()</code>. If it
* is a numeric IPv6 address, it can optionally be enclosed in
* square brackets <code>[]</code>.
*
* @param port the port part of the URL.
*
* @param urlPath the URL path part of the URL. If null, defaults to
* the empty string.
*
* @exception MalformedURLException if one of the parts is
* syntactically incorrect, or if <code>host</code> is null and it
* is not possible to find the local host name, or if
* <code>port</code> is negative.
*/
throws MalformedURLException {
protocol = "jmxmp";
try {
} catch (UnknownHostException e) {
throw new MalformedURLException("Local host name unknown: " +
e);
}
/* 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 {
} catch (MalformedURLException e) {
"Replacing illegal local host name " +
host + " with numeric IP address " +
"(see RFC 1034)", e);
}
/* Use the numeric address, which could be either IPv4
or IPv6. validateHost will accept either. */
}
}
throw new MalformedURLException("Host starts with [ but " +
"does not end with ]");
}
if (!isNumericIPv6Address(host)) {
throw new MalformedURLException("Address inside [...] must " +
"be numeric IPv6 address");
}
throw new MalformedURLException("More than one [[...]]");
}
urlPath = "";
validate();
}
// Check protocol
throw new MalformedURLException("Missing or invalid protocol " +
}
// Check host
validateHost();
// Check port
if (port < 0)
// Check URL path
}
}
if (port != 0) {
throw new MalformedURLException("Cannot give port number " +
"without host name");
}
return;
}
}
throws MalformedURLException {
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 ":". */
new MalformedURLException("Bad IPv6 address: " + h);
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. */
char lastc = '.';
boolean sawDot = false;
char componentStart = 0;
loop:
for (int i = 0; i < hostLen; i++) {
char c = h.charAt(i);
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 {
break;
}
}
try {
if (lastc != 'a')
throw randomException;
/* 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. */
for (int i = 0; i < 4; i++) {
if (n < 0 || n > 255)
throw randomException;
throw randomException;
}
if (tok.hasMoreTokens())
throw randomException;
}
} catch (Exception e) {
}
}
}
/**
* <p>The protocol part of the Service URL.
*
* @return the protocol part of the Service URL. This is never null.
*/
return protocol;
}
/**
* <p>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
* <code>InetAddress.getLocalHost().getHostName()</code>.</p>
*
* <p>In either case, if the host was specified using the
* <code>[...]</code> syntax for numeric IPv6 addresses, the
* square brackets are not included in the return value here.</p>
*
* @return the host part of the Service URL. This is never null.
*/
return host;
}
/**
* <p>The port of the Service URL. If no port was
* specified, the returned value is 0.</p>
*
* @return the port of the Service URL, or 0 if none.
*/
public int getPort() {
return port;
}
/**
* <p>The URL Path part of the Service URL. This is an empty
* string, or a string beginning with a slash (<code>/</code>), or
* a string beginning with a semicolon (<code>;</code>).
*
* @return the URL Path part of the Service URL. This is never
* null.
*/
return urlPath;
}
/**
* <p>The string representation of this Service URL. If the value
* returned by this method is supplied to the
* <code>JMXServiceURL</code> constructor, the resultant object is
* equal to this one.</p>
*
* <p>The <code><em>host</em></code> 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 <code>[]</code>.</p>
*
* <p>The <code><em>port</em></code> 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.</p>
*
* @return the string representation of this Service URL.
*/
/* We don't bother synchronizing the access to toString. At worst,
n threads will independently compute and store the same value. */
return toString;
if (isNumericIPv6Address(getHost))
else
if (getPort != 0)
return toString;
}
/**
* <p>Indicates whether some other object is equal to this one.
* This method returns true if and only if <code>obj</code> is an
* instance of <code>JMXServiceURL</code> 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.
*
* @param obj the reference object with which to compare.
*
* @return <code>true</code> if this object is the same as the
* <code>obj</code> argument; <code>false</code> otherwise.
*/
if (!(obj instanceof JMXServiceURL))
return false;
return
}
public int hashCode() {
}
/* True if this string, assumed to be a valid argument to
* InetAddress.getByName, is a numeric IPv6 address.
*/
// address contains colon if and only if it's a numeric IPv6 address
}
// like String.indexOf but returns string length not -1 if not present
if (index < 0)
return s.length();
else
return index;
}
int fromIndex) {
int i = fromIndex;
while (true) {
if (i >= slen)
break;
char c = s.charAt(i);
if (c >= 128)
break; // not ASCII
break;
i++;
}
return i;
}
static {
/* 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);
}
/**
* The value returned by {@link #getProtocol()}.
*/
/**
* The value returned by {@link #getHost()}.
*/
/**
* The value returned by {@link #getPort()}.
*/
private final int port;
/**
* The value returned by {@link #getURLPath()}.
*/
/**
* Cached result of {@link #toString()}.
*/
}