001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase; 020 021import java.io.IOException; 022import java.lang.management.ManagementFactory; 023import java.rmi.registry.LocateRegistry; 024import java.rmi.registry.Registry; 025import java.rmi.server.RMIClientSocketFactory; 026import java.rmi.server.RMIServerSocketFactory; 027import java.rmi.server.UnicastRemoteObject; 028import java.util.HashMap; 029import javax.management.MBeanServer; 030import javax.management.remote.JMXConnectorServer; 031import javax.management.remote.JMXConnectorServerFactory; 032import javax.management.remote.JMXServiceURL; 033import javax.management.remote.rmi.RMIConnectorServer; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 036import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 037import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 038import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessor; 039import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; 040import org.apache.yetus.audience.InterfaceAudience; 041import org.slf4j.Logger; 042import org.slf4j.LoggerFactory; 043 044/** 045 * Pluggable JMX Agent for HBase(to fix the 2 random TCP ports issue 046 * of the out-of-the-box JMX Agent): 047 * 1)connector port can share with the registry port if SSL is OFF 048 * 2)support password authentication 049 * 3)support subset of SSL (with default configuration) 050 */ 051@InterfaceAudience.Private 052public class JMXListener implements MasterCoprocessor, RegionServerCoprocessor { 053 private static final Logger LOG = LoggerFactory.getLogger(JMXListener.class); 054 public static final String RMI_REGISTRY_PORT_CONF_KEY = ".rmi.registry.port"; 055 public static final String RMI_CONNECTOR_PORT_CONF_KEY = ".rmi.connector.port"; 056 public static final int defMasterRMIRegistryPort = 10101; 057 public static final int defRegionserverRMIRegistryPort = 10102; 058 059 /** 060 * workaround for HBASE-11146 061 * master and regionserver are in 1 JVM in standalone mode 062 * only 1 JMX instance is allowed, otherwise there is port conflict even if 063 * we only load regionserver coprocessor on master 064 */ 065 private static JMXConnectorServer JMX_CS = null; 066 private Registry rmiRegistry = null; 067 068 public static JMXServiceURL buildJMXServiceURL(int rmiRegistryPort, 069 int rmiConnectorPort) throws IOException { 070 // Build jmxURL 071 StringBuilder url = new StringBuilder(); 072 url.append("service:jmx:rmi://localhost:"); 073 url.append(rmiConnectorPort); 074 url.append("/jndi/rmi://localhost:"); 075 url.append(rmiRegistryPort); 076 url.append("/jmxrmi"); 077 078 return new JMXServiceURL(url.toString()); 079 080 } 081 082 public void startConnectorServer(int rmiRegistryPort, int rmiConnectorPort) 083 throws IOException { 084 boolean rmiSSL = false; 085 boolean authenticate = true; 086 String passwordFile = null; 087 String accessFile = null; 088 089 System.setProperty("java.rmi.server.randomIDs", "true"); 090 091 String rmiSSLValue = System.getProperty("com.sun.management.jmxremote.ssl", 092 "false"); 093 rmiSSL = Boolean.parseBoolean(rmiSSLValue); 094 095 String authenticateValue = 096 System.getProperty("com.sun.management.jmxremote.authenticate", "false"); 097 authenticate = Boolean.parseBoolean(authenticateValue); 098 099 passwordFile = System.getProperty("com.sun.management.jmxremote.password.file"); 100 accessFile = System.getProperty("com.sun.management.jmxremote.access.file"); 101 102 LOG.info("rmiSSL:" + rmiSSLValue + ",authenticate:" + authenticateValue 103 + ",passwordFile:" + passwordFile + ",accessFile:" + accessFile); 104 105 // Environment map 106 HashMap<String, Object> jmxEnv = new HashMap<>(); 107 108 RMIClientSocketFactory csf = null; 109 RMIServerSocketFactory ssf = null; 110 111 if (rmiSSL) { 112 if (rmiRegistryPort == rmiConnectorPort) { 113 throw new IOException("SSL is enabled. " + 114 "rmiConnectorPort cannot share with the rmiRegistryPort!"); 115 } 116 csf = new SslRMIClientSocketFactorySecure(); 117 ssf = new SslRMIServerSocketFactorySecure(); 118 } 119 120 if (csf != null) { 121 jmxEnv.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf); 122 } 123 if (ssf != null) { 124 jmxEnv.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf); 125 } 126 127 // Configure authentication 128 if (authenticate) { 129 jmxEnv.put("jmx.remote.x.password.file", passwordFile); 130 jmxEnv.put("jmx.remote.x.access.file", accessFile); 131 } 132 133 // Create the RMI registry 134 rmiRegistry = LocateRegistry.createRegistry(rmiRegistryPort); 135 // Retrieve the PlatformMBeanServer. 136 MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 137 138 // Build jmxURL 139 JMXServiceURL serviceUrl = buildJMXServiceURL(rmiRegistryPort, rmiConnectorPort); 140 141 try { 142 // Start the JMXListener with the connection string 143 synchronized(JMXListener.class) { 144 if (JMX_CS != null) { 145 throw new RuntimeException("Started by another thread?"); 146 } 147 JMX_CS = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, jmxEnv, mbs); 148 JMX_CS.start(); 149 } 150 LOG.info("JMXConnectorServer started!"); 151 } catch (IOException e) { 152 LOG.error("Failed start of JMXConnectorServer!", e); 153 // deregister the RMI registry 154 if (rmiRegistry != null) { 155 UnicastRemoteObject.unexportObject(rmiRegistry, true); 156 } 157 } 158 159 } 160 161 public void stopConnectorServer() throws IOException { 162 synchronized (JMXListener.class) { 163 if (JMX_CS != null) { 164 JMX_CS.stop(); 165 LOG.info("ConnectorServer stopped!"); 166 JMX_CS = null; 167 } 168 // deregister the RMI registry 169 if (rmiRegistry != null) { 170 UnicastRemoteObject.unexportObject(rmiRegistry, true); 171 } 172 } 173 } 174 175 176 @Override 177 public void start(CoprocessorEnvironment env) throws IOException { 178 int rmiRegistryPort = -1; 179 int rmiConnectorPort = -1; 180 Configuration conf = env.getConfiguration(); 181 182 if (env instanceof MasterCoprocessorEnvironment) { 183 // running on Master 184 rmiRegistryPort = 185 conf.getInt("master" + RMI_REGISTRY_PORT_CONF_KEY, defMasterRMIRegistryPort); 186 rmiConnectorPort = conf.getInt("master" + RMI_CONNECTOR_PORT_CONF_KEY, rmiRegistryPort); 187 LOG.info("Master rmiRegistryPort:" + rmiRegistryPort + ",Master rmiConnectorPort:" 188 + rmiConnectorPort); 189 } else if (env instanceof RegionServerCoprocessorEnvironment) { 190 // running on RegionServer 191 rmiRegistryPort = 192 conf.getInt("regionserver" + RMI_REGISTRY_PORT_CONF_KEY, 193 defRegionserverRMIRegistryPort); 194 rmiConnectorPort = 195 conf.getInt("regionserver" + RMI_CONNECTOR_PORT_CONF_KEY, rmiRegistryPort); 196 LOG.info("RegionServer rmiRegistryPort:" + rmiRegistryPort 197 + ",RegionServer rmiConnectorPort:" + rmiConnectorPort); 198 199 } else if (env instanceof RegionCoprocessorEnvironment) { 200 LOG.error("JMXListener should not be loaded in Region Environment!"); 201 return; 202 } 203 204 synchronized(JMXListener.class) { 205 if (JMX_CS != null) { 206 LOG.info("JMXListener has been started at Registry port " + rmiRegistryPort); 207 } 208 else { 209 startConnectorServer(rmiRegistryPort, rmiConnectorPort); 210 } 211 } 212 } 213 214 @Override 215 public void stop(CoprocessorEnvironment env) throws IOException { 216 stopConnectorServer(); 217 } 218 219}