http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e6ac83be/utils/jmx/jmxrmi-agent/src/main/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiAgent.java ---------------------------------------------------------------------- diff --git a/utils/jmx/jmxrmi-agent/src/main/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiAgent.java b/utils/jmx/jmxrmi-agent/src/main/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiAgent.java new file mode 100644 index 0000000..6aa35d3 --- /dev/null +++ b/utils/jmx/jmxrmi-agent/src/main/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiAgent.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.util.jmx.jmxrmi; + +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.rmi.registry.LocateRegistry; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +import javax.management.MBeanServer; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXServiceURL; + +import org.apache.brooklyn.util.jmx.jmxrmi.JmxRmiAgent; + +/** + * This exposes JMX support for going through firewalls by starting an RMI registry server + * on a well-known port. + * <p> + * This implementation DOES NOT support port-forwarding however. The same hostname used internally + * (specified in {@link #RMI_HOSTNAME_PROPERTY} or autodetected by java) must also be addressable + * by the JMX client. This is due to how the property is used internally by java during the + * RMI registry re-direction. + * <p> + * If you require that the client connects to a different hostname/IP than the one where the + * service is bound, consider using the Brooklyn JmxmpAgent, as this will not work! + * <p> + * This listens on {@value #RMI_REGISTRY_PORT_PROPERTY} unless overridden by system property + * {@link #RMI_REGISTRY_PORT_PROPERTY} ({@value #RMI_REGISTRY_PORT_PROPERTY}). + * + * @see brooklyn.util.jmx.jmxmp.JmxmpAgent + * @see https://blogs.oracle.com/jmxetc/entry/connecting_through_firewall_using_jmx + * @see https://blogs.oracle.com/jmxetc/entry/more_on_premain_and_jmx + */ +public class JmxRmiAgent { + + /** Port for RMI registry to listen on. Default to {@link #RMI_REGISTRY_DEFAULT_PORT}. */ + public static final String RMI_REGISTRY_PORT_PROPERTY = "brooklyn.jmx-agent.rmi-port"; + public static final String RMI_REGISTRY_DEFAULT_PORT = "9001"; + + /** Port for JMX server (sometimes called JMX_RMI server) to listen on. Default to {@link #JMX_SERVER_DEFAULT_PORT}. */ + public static final String JMX_SERVER_PORT_PROPERTY = "brooklyn.jmx-agent.jmx-port"; + public static final String JMX_SERVER_DEFAULT_PORT = "11099"; + + /** Hostname to advertise, and if {@value #JMX_SERVER_ADDRESS_WILDCARD_PROPERTY} is false also the hostname/interface to bind to. + * Should never be 0.0.0.0 as it is publicly advertised. */ + public static final String RMI_HOSTNAME_PROPERTY = "java.rmi.server.hostname"; + + /** Whether JMX should bind to all interfaces. */ + public static final String JMX_SERVER_ADDRESS_WILDCARD_PROPERTY = "jmx.remote.server.address.wildcard"; + + /** + * The entry point, uses the JDK dynamic agent loading feature. + */ + public static void premain(String agentArgs) { + doMain(agentArgs); + } + + public static void agentmain(String agentArgs) { + doMain(agentArgs); + } + + public static void doMain(final String agentArgs) { + // taken from JmxmpAgent in sister project + + // do the work in a daemon thread so that if the main class terminates abnormally, + // such that shutdown hooks aren't called, we don't keep the application running + // (e.g. if the app is compiled with java7 then run with java6, with a java6 agent here; + // that causes the agent to launch, the main to fail, but the process to keep going) + Thread t = new Thread() { + public void run() { + doMainForeground(agentArgs); + } + }; + t.setDaemon(true); + t.start(); + } + + public static void doMainForeground(String agentArgs) { + final JMXConnectorServer connector = new JmxRmiAgent().startServer(System.getProperties()); + if (connector != null) { + Runtime.getRuntime().addShutdownHook(new Thread("jmxrmi-agent-shutdownHookThread") { + @Override public void run() { + try { + connector.stop(); + } catch (Exception e) { + System.err.println("Error closing jmxrmi connector in shutdown hook (continuing): "+e); + } + }}); + } + } + + public JMXConnectorServer startServer(Properties properties) { + try { + // Ensure cryptographically strong random number generator used + // to choose the object number - see java.rmi.server.ObjID + System.setProperty("java.rmi.server.randomIDs", "true"); + + // Start an RMI registry on port specified + final int rmiPort = Integer.parseInt(System.getProperty(RMI_REGISTRY_PORT_PROPERTY, RMI_REGISTRY_DEFAULT_PORT)); + final int jmxPort = Integer.parseInt(System.getProperty(JMX_SERVER_PORT_PROPERTY, JMX_SERVER_DEFAULT_PORT)); + final String hostname = getLocalhostHostname(properties); + + System.out.println("Setting up JmxRmiAgent for: "+hostname+" "+rmiPort+" / "+jmxPort); + + LocateRegistry.createRegistry(rmiPort); + + // Retrieve the PlatformMBeanServer. + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + + // Environment map. + Map<String, Object> env = new LinkedHashMap<String, Object>(); + propagate(properties, env, JMX_SERVER_ADDRESS_WILDCARD_PROPERTY, "true"); + + // TODO Security + + // Create an RMI connector server. + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://" + hostname + ":" + jmxPort + "/jndi/rmi://" + hostname + ":" + rmiPort + "/jmxrmi"); + + // Now create the server from the JMXServiceURL + JMXConnectorServer connector = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); + + // Start the RMI connector server. + connector.start(); + System.out.println("JmxRmiAgent JMXConnectorServer active at: " + url); + + return connector; + } catch (RuntimeException e) { + System.err.println("Unable to start JMXConnectorServer: " + e); + throw e; + } catch (Exception e) { + System.err.println("Unable to start JMXConnectorServer: " + e); + throw new RuntimeException(e); + } + } + + /** + * Copies the value of key from the source to the target, if set. Otherwise + * sets the {@code defaultValueIfNotNull} if that is not null. + * + * @return whether anything is set + */ + private static boolean propagate(Properties source, Map<String, Object> target, String key, Object defaultValueIfNotNull) { + Object v = source.getProperty(key); + if (v == null) v = defaultValueIfNotNull; + if (v == null) return false; + target.put(key, v); + return true; + } + + private String getLocalhostHostname(Properties properties) throws UnknownHostException { + String hostname = properties == null ? null : properties.getProperty(RMI_HOSTNAME_PROPERTY); + if ("0.0.0.0".equals(hostname)) { + System.err.println("WARN: invalid hostname 0.0.0.0 specified for JmxRmiAgent; " + + "it typically must be an address or hostname which is bindable on the machine where " + + "this service is running AND accessible by a client machine (access will likely be impossible)"); + } + if (hostname == null || hostname.isEmpty()) { + hostname = InetAddress.getLocalHost().getHostName(); + } + return hostname; + } + + /** + * Convenience main method. + */ + public static void main(String[] args) throws Exception { + premain(""); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e6ac83be/utils/jmx/jmxrmi-agent/src/test/java/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java ---------------------------------------------------------------------- diff --git a/utils/jmx/jmxrmi-agent/src/test/java/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java b/utils/jmx/jmxrmi-agent/src/test/java/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java deleted file mode 100644 index 577d352..0000000 --- a/utils/jmx/jmxrmi-agent/src/test/java/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package brooklyn.util.jmx.jmxrmi; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.Map; - -import javax.management.MBeanServerConnection; -import javax.management.remote.JMXConnector; -import javax.management.remote.JMXConnectorFactory; -import javax.management.remote.JMXServiceURL; - -@SuppressWarnings({"rawtypes","unchecked"}) -public class JmxRmiClient { - - public void connect(String urlString, Map env) throws MalformedURLException, IOException { - JMXServiceURL url = new JMXServiceURL(urlString); - System.out.println("JmxmpClient connecting to "+url); - JMXConnector jmxc = JMXConnectorFactory.connect(url, env); - - MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); - String domains[] = mbsc.getDomains(); - for (int i = 0; i < domains.length; i++) { - System.out.println("Domain[" + i + "] = " + domains[i]); - } - - jmxc.close(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/e6ac83be/utils/jmx/jmxrmi-agent/src/test/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java ---------------------------------------------------------------------- diff --git a/utils/jmx/jmxrmi-agent/src/test/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java b/utils/jmx/jmxrmi-agent/src/test/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java new file mode 100644 index 0000000..29b5de7 --- /dev/null +++ b/utils/jmx/jmxrmi-agent/src/test/java/org/apache/brooklyn/util/jmx/jmxrmi/JmxRmiClient.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.brooklyn.util.jmx.jmxrmi; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Map; + +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +@SuppressWarnings({"rawtypes","unchecked"}) +public class JmxRmiClient { + + public void connect(String urlString, Map env) throws MalformedURLException, IOException { + JMXServiceURL url = new JMXServiceURL(urlString); + System.out.println("JmxmpClient connecting to "+url); + JMXConnector jmxc = JMXConnectorFactory.connect(url, env); + + MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); + String domains[] = mbsc.getDomains(); + for (int i = 0; i < domains.length; i++) { + System.out.println("Domain[" + i + "] = " + domains[i]); + } + + jmxc.close(); + } + +}
