/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package javax.management.remote; import java.net.MalformedURLException; import java.io.Serializable; import org.jboss.logging.Logger; /** * @author <a href="mailto:tom@jboss.org">Tom Elrod</a> */ public class JMXServiceURL implements Serializable { private static final long serialVersionUID = 8173364409860779292l; /** * Per spec, every jmx service url must start with this prefix string. */ public static final String REQUIRED_URL_PREFIX = "service:jmx:"; private static final int DEFAULT_PORT = 0; //TODO: -TME How to know what/if default port should be set to (JBREM-149) private String protocol = null; private String host = null; private int port = DEFAULT_PORT; private String urlPath = ""; private transient String stringServiceURL = null; protected transient Logger log = Logger.getLogger(getClass()); public JMXServiceURL(String serviceURL) throws MalformedURLException { if(serviceURL != null) { if(serviceURL.startsWith(REQUIRED_URL_PREFIX)) { if(log.isTraceEnabled()) { log.trace("Start parsing of jmx service url: " + serviceURL); } // begin parsing rest of the url, minus the prefix parseCoreURL(serviceURL.substring(REQUIRED_URL_PREFIX.length())); } else { throw new MalformedURLException("Can not create JMXServiceURL due to provided service url not starting with " + REQUIRED_URL_PREFIX + ". URL String provided was: " + serviceURL); } } else { throw new NullPointerException("Can not create JMXServiceURL due to constructor parameter being null."); } } public JMXServiceURL(String protocol, String host, int port) throws MalformedURLException { this(protocol, host, port, null); } public JMXServiceURL(String protocol, String host, int port, String urlPath) throws MalformedURLException { setProtocol(protocol); setHost(host); setPort(port); setPath(urlPath); } /** * Will parse the service url with the jmx service prefix removed working from left to right. * * @param serviceURL */ private void parseCoreURL(String serviceURL) throws MalformedURLException { // first look for protocol, which will end with '://' final String protocolSeperator = "://"; int index = serviceURL.indexOf(protocolSeperator); if(index == -1) { log.error("Error parsing core jmx service url. Could not find protocol within string " + serviceURL); throw new MalformedURLException("Error in parsing JMX service URL. Does not properly define protocol."); } String invalidatedProtocol = serviceURL.substring(0, index); setProtocol(validateProtocol(invalidatedProtocol)); // next is host and port String hostPlus = serviceURL.substring(index + protocolSeperator.length()); String path = parseHostAndPort(hostPlus); if(path != null && path.length() > 0) { setPath(validatePath(path)); } log.debug("Have parsed jmx service url into following values - protocol: " + getProtocol() + ", host: " + getHost() + ", port: " + getPort() + ", path: " + getURLPath()); } private String validatePath(String path) throws MalformedURLException { if(!path.startsWith("/") && !path.startsWith(";")) { throw new MalformedURLException("Error parsing JMX service URL. " + "The path did not begin with either a '/' or ';' character."); } else { return path; } } private void setPath(String path) { if(path == null) { this.urlPath = ""; } else { this.urlPath = path; } } private String parseHostAndPort(String hostPlus) throws MalformedURLException { String pathString = null; int index = -1; // now check to see if is path seperator. per spec, seperator can be either / or ; index = hostPlus.indexOf(';'); index = index == -1 ? hostPlus.indexOf('/') : index; // capture path info is available if(index != -1) { setHost(null); pathString = hostPlus.substring(index); } hostPlus = index == -1 ? hostPlus : hostPlus.substring(0, index); if(hostPlus.length() != 0) { int endOfHostIndex = -1; // first need to check if host is IPv6, which will start with bracket if(hostPlus.charAt(0) == '[') { endOfHostIndex = hostPlus.lastIndexOf(']'); if(endOfHostIndex == -1) { log.error("Could not process service url section " + hostPlus + " due to the host section " + "starting with [ but no ending ] before the path seperator."); throw new MalformedURLException("Error parsing JMX service URL. Host started with [, but did not have an end ]."); } String ipv6Host = hostPlus.substring(1, endOfHostIndex); setHost(ipv6Host); endOfHostIndex++; // need to move index to next character after ] } else // should be dealing with IPv4 then { endOfHostIndex = hostPlus.indexOf(':'); if(endOfHostIndex != -1) { // means that no host specified if(endOfHostIndex == 0) { throw new MalformedURLException("Error parsing JMX service URL. Port specified, but not host."); } else { setHost(hostPlus.substring(0, endOfHostIndex)); } } else { setHost(hostPlus); } } // now onto port, any leftover characters for port if(endOfHostIndex != -1 && hostPlus.length() > (endOfHostIndex)) { String portString = hostPlus.substring(endOfHostIndex); if(portString.charAt(0) != ':') { log.error("Error parsing host and port of jmx service url. Host and port must be seperated by ':'. " + "Offending serviceURL section is " + hostPlus); throw new MalformedURLException("Error in parsing JMX service URL. Host and port must be seperated by ':'."); } else { if(getHost() == null || getHost().length() == 0) { throw new MalformedURLException("Error parsing JMX service URL. " + "Can not have port specified without having a host specified as well."); } String invalidatedPort = portString.substring(1); setPort(validatePort(invalidatedPort)); } } } return pathString; } private void setHost(String serviceHost) { if(serviceHost == null) { /* try { this.host = InetAddress.getLocalHost().getHostName(); } catch(UnknownHostException e) { log.error("Error getting local host name to return as host.", e); throw new RuntimeException("Error getting local host name to return as JMX Service host.", e); } */ this.host = null; } else { // check to see if is ipv6, which may start and end with bracket if(serviceHost.charAt(0) == '[' && serviceHost.charAt(serviceHost.length() - 1) == ']') { this.host = serviceHost.substring(1, serviceHost.length() - 1); } else { this.host = serviceHost; } } } private void setPort(int port) { this.port = port; } /** * Will convert from string to int. If value is 0, will set return value to DEFAULT_PORT. * * @param invalidatedPort * @return * @throws MalformedURLException */ private int validatePort(String invalidatedPort) throws MalformedURLException { int validatedPort = DEFAULT_PORT; try { validatedPort = Integer.parseInt(invalidatedPort); if(validatedPort == 0) { validatedPort = DEFAULT_PORT; } } catch(NumberFormatException e) { throw new MalformedURLException("Error converting JMX Service url port (" + invalidatedPort + ") to a numeric value."); } return validatedPort; } private void setProtocol(String protocol) throws MalformedURLException { this.protocol = validateProtocol(protocol); } private String validateProtocol(String protocol) throws MalformedURLException { if(protocol == null || protocol.length() == 0) { return "jmxmp"; } // now check for valid ASCII characters for(int x = 0; x < protocol.length(); x++) { char c = protocol.charAt(x); // per spec, protocol must be one of the following: // +, -, 0-9, A-Z, or a-z if(c != 43 && c != 45 && !(c >= 48 && c <= 57) && !(c >= 65 && c <= 90) && !(c >= 97 && c <= 122)) { throw new MalformedURLException("Error in parsing JMX service ULR protocol because " + "contains no valid character 0x" + Integer.toHexString(c)); } } return protocol.toLowerCase(); } public String getProtocol() { return protocol; } public String getHost() { return host; } public int getPort() { return port; } public String getURLPath() { return urlPath; } public String toString() { if(stringServiceURL == null) { // have to compose formal service url StringBuffer buffer = new StringBuffer(REQUIRED_URL_PREFIX); buffer.append(getProtocol()); buffer.append("://"); String host = getHost(); if(host != null) { // have to brackets around if ipv6 (which should contain : somewhere) if(host.indexOf(':') != -1) { buffer.append("["); buffer.append(host); buffer.append("]"); } else { buffer.append(host); } } int port = getPort(); if(port > 0) { buffer.append(":"); buffer.append(port); } buffer.append(getURLPath()); stringServiceURL = buffer.toString(); } return stringServiceURL; } public boolean equals(Object obj) { if(obj instanceof JMXServiceURL) { JMXServiceURL jsu = (JMXServiceURL) obj; if(getProtocol().equalsIgnoreCase(jsu.getProtocol()) && getPort() == jsu.getPort()) { String host = getHost(); if(host == null) { if(jsu.getHost() != null) { return false; } else { String urlPath = getURLPath(); if(urlPath == null && jsu.getURLPath() != null) { return false; } else { return urlPath.equals(jsu.getURLPath()); } } } else { if(host.equals(jsu.getHost())) { String urlPath = getURLPath(); if(urlPath == null && jsu.getURLPath() != null) { return false; } else { return urlPath.equals(jsu.getURLPath()); } } else { return false; } } } else { return false; } } else { return false; } } public int hashCode() { return toString().hashCode(); } }