| /* |
| * 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.synapse; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.synapse.securevault.PasswordManager; |
| import org.apache.synapse.securevault.secret.SecretInformation; |
| import org.apache.synapse.commons.util.RMIRegistryController; |
| import org.apache.synapse.commons.jmx.JmxInformation; |
| import org.apache.synapse.commons.jmx.JmxSecretAuthenticator; |
| |
| import javax.management.MBeanServer; |
| import javax.management.remote.JMXConnectorServer; |
| import javax.management.remote.JMXConnectorServerFactory; |
| import javax.management.remote.JMXServiceURL; |
| import javax.rmi.ssl.SslRMIClientSocketFactory; |
| import javax.rmi.ssl.SslRMIServerSocketFactory; |
| |
| import java.io.IOException; |
| import java.lang.management.ManagementFactory; |
| import java.net.InetAddress; |
| import java.net.UnknownHostException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * JMX Adaptor class providing a JMX server connector to be able to query MBeans via JConsole or any |
| * other JMX-compatible management solution.<br> |
| * The provided JNDI port will be used to create a local RMI registry. If no RMI port will be |
| * provided dynamic RMI ports will be used for remote MBean queries.<br> |
| * If the JMXAdaptor shall be used in a firewalled environment, additionally a fixed RMI port should |
| * be provided and both ports should be opened in the firewall.<br> |
| * JMX URL used if only JNDI port is provided:<br> |
| * <code>service:jmx:rmi:///jndi/rmi://<hostname>:<jndiPort>/synapse</code><br> |
| * JMX URL used if JNDI port and RMI port are provided:<br> |
| * <code>service:jmx:rmi://<hostname>:<rmiPort>/jndi/rmi://<hostname>:<jndiPort>/synapse</code><br> |
| */ |
| public class JmxAdapter { |
| |
| /** |
| * Logger of this class. |
| */ |
| private static Log log = LogFactory.getLog(JmxAdapter.class); |
| |
| /** |
| * Base port to start with if automatic free port detection is used (default). Configurable in |
| * synapse.properties via synapse.jmx.jndiPort=0. |
| */ |
| private static final int JNDI_AUTO_PORT_OFFSET = 1099; |
| |
| /** |
| * Encapsulates all information needed to configure the JMX Adapter. |
| */ |
| private JmxInformation jmxInformation; |
| |
| /** |
| * @see JMXConnectorServer |
| */ |
| private JMXConnectorServer connectorServer; |
| |
| /** |
| * Creates a new instance of a JMX Adaptor using the provided JMX information. |
| * |
| * @param jmxInformation any JMX related information |
| */ |
| public JmxAdapter(JmxInformation jmxInformation) { |
| this.jmxInformation = jmxInformation; |
| } |
| |
| /** |
| * Lazily creates the RMI registry and starts the JMX connector server based on the |
| * |
| * @throws SynapseException if the JMX configuration is erroneous and/or the connector server |
| * cannot be started |
| */ |
| public void start() { |
| initConfiguration(); |
| |
| try { |
| boolean registryCreated = false; |
| int jndiPort = jmxInformation.getJndiPort(); |
| |
| // automatic detection starting at base port |
| if (jndiPort == 0) { |
| jndiPort = JNDI_AUTO_PORT_OFFSET; |
| for (int retries = 0; !registryCreated && (retries < 100); retries++) { |
| try { |
| RMIRegistryController.getInstance().createLocalRegistry(jndiPort); |
| registryCreated = true; |
| } catch (Exception ignored) { |
| jndiPort++; |
| log.warn("Trying alternate port " + jndiPort); |
| } |
| } |
| jmxInformation.setJndiPort(jndiPort); |
| } else { |
| RMIRegistryController.getInstance().createLocalRegistry(jndiPort); |
| registryCreated = true; |
| } |
| |
| if (registryCreated) { |
| jmxInformation.updateJMXUrl(); |
| JMXServiceURL url = new JMXServiceURL(jmxInformation.getJmxUrl()); |
| Map<String, Object> env = createContextMap(); |
| MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); |
| connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); |
| try { |
| connectorServer.start(); |
| } catch (IOException ex) { |
| log.warn("Cannot start JMXConnectorServer on " + jmxInformation.getJmxUrl(), ex); |
| } |
| } |
| } catch (Exception ex) { |
| log.error("Error while setting up remote JMX connector", ex); |
| } |
| } |
| |
| /** |
| * Stops the JMX connector server. |
| */ |
| public void stop() { |
| if (connectorServer != null) { |
| log.info("JMXConnectorServer stopping on " + jmxInformation.getJmxUrl()); |
| try { |
| connectorServer.stop(); |
| RMIRegistryController.getInstance().removeLocalRegistry(jmxInformation.getJndiPort()); |
| jmxInformation = null; |
| } catch (IOException ex) { |
| log.error("Error while stopping remote JMX connector", ex); |
| } |
| connectorServer = null; |
| } |
| } |
| |
| /** |
| * Initialized the JMX configuration. |
| * |
| * @throws SynapseException if the port or host configuration is erroneous |
| */ |
| private void initConfiguration() { |
| int jndiPort = jmxInformation.getJndiPort(); |
| if ((jndiPort < 0) || (65535 < jndiPort)) { |
| throw new SynapseException("JNDI Port for Remote Registry not properly configured"); |
| } |
| |
| int rmiPort = jmxInformation.getRmiPort(); |
| if ((rmiPort < 0) || (65535 < rmiPort)) { |
| rmiPort = 0; |
| log.info("No or invalid value specified for JMX RMI port - using dynamic port"); |
| } |
| |
| String hostname = jmxInformation.getHostName(); |
| if ((hostname == null) || (hostname.trim().length() == 0)) { |
| try { |
| InetAddress address = InetAddress.getLocalHost(); |
| jmxInformation.setHostName(address.getHostName()); |
| } catch (UnknownHostException ex) { |
| throw new SynapseException("Hostname of loopback could not be determined", ex); |
| } |
| } |
| } |
| |
| /** |
| * Determines whether the JMX Connector server has been started and is running. |
| * |
| * @return true, if the connector server is running, otherwise false |
| */ |
| public boolean isRunning() { |
| return connectorServer != null && connectorServer.isActive(); |
| } |
| |
| public JmxInformation getJmxInformation() { |
| return jmxInformation; |
| } |
| |
| public void setJmxInformation(JmxInformation jmxInformation) { |
| this.jmxInformation = jmxInformation; |
| } |
| |
| /** |
| * Creates an environment context map containing the configuration used to start the |
| * server connector. |
| * |
| * @return an environment context map containing the configuration used to start the server |
| * connector |
| */ |
| private Map<String, Object> createContextMap() { |
| Map<String, Object> env = new HashMap<String, Object>(); |
| |
| if (jmxInformation.isAuthenticate()) { |
| |
| if (jmxInformation.getRemotePasswordFile() != null) { |
| env.put("jmx.remote.x.password.file", jmxInformation.getRemotePasswordFile()); |
| } else { |
| SecretInformation secretInformation = jmxInformation.getSecretInformation(); |
| // Get the global secret resolver |
| //TODO This should be properly implemented if JMX adapter is going to use out side synapse |
| PasswordManager pwManager = PasswordManager.getInstance(); |
| if (pwManager.isInitialized()) { |
| secretInformation.setGlobalSecretResolver(pwManager.getSecretResolver()); |
| } |
| env.put(JMXConnectorServer.AUTHENTICATOR, |
| new JmxSecretAuthenticator(jmxInformation.getSecretInformation())); |
| } |
| |
| if (jmxInformation.getRemoteAccessFile() != null) { |
| env.put("jmx.remote.x.access.file", jmxInformation.getRemoteAccessFile()); |
| } |
| } |
| |
| if (jmxInformation.isRemoteSSL()) { |
| log.info("Activated SSL communication"); |
| env.put("jmx.remote.rmi.client.socket.factory", new SslRMIClientSocketFactory()); |
| env.put("jmx.remote.rmi.server.socket.factory", new SslRMIServerSocketFactory()); |
| } |
| |
| return env; |
| } |
| } |