View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.hadoop.conf.Configuration;
24  import org.apache.hadoop.hbase.CoprocessorEnvironment;
25  import org.apache.hadoop.hbase.coprocessor.*;
26  
27  import java.io.IOException;
28  import java.lang.management.ManagementFactory;
29  import java.rmi.registry.LocateRegistry;
30  import java.rmi.server.RMIClientSocketFactory;
31  import java.rmi.server.RMIServerSocketFactory;
32  import java.util.HashMap;
33  
34  import javax.management.MBeanServer;
35  import javax.management.remote.JMXConnectorServer;
36  import javax.management.remote.JMXConnectorServerFactory;
37  import javax.management.remote.JMXServiceURL;
38  import javax.management.remote.rmi.RMIConnectorServer;
39  import javax.rmi.ssl.SslRMIClientSocketFactory;
40  import javax.rmi.ssl.SslRMIServerSocketFactory;
41  
42  /**
43   * Pluggable JMX Agent for HBase(to fix the 2 random TCP ports issue
44   * of the out-of-the-box JMX Agent):
45   * 1)connector port can share with the registry port if SSL is OFF
46   * 2)support password authentication
47   * 3)support subset of SSL (with default configuration)
48   */
49  public class JMXListener implements Coprocessor {
50  
51    public static final Log LOG = LogFactory.getLog(JMXListener.class);
52    public static final String RMI_REGISTRY_PORT_CONF_KEY = ".rmi.registry.port";
53    public static final String RMI_CONNECTOR_PORT_CONF_KEY = ".rmi.connector.port";
54    public static final int defRMIRegistryPort = 10102;
55  
56    /**
57     * workaround for HBASE-11146
58     * master and regionserver are in 1 JVM in standalone mode
59     * only 1 JMX instance is allowed, otherwise there is port conflict even if
60     * we only load regionserver coprocessor on master
61     */
62    private static JMXConnectorServer jmxCS = null;
63  
64    public static JMXServiceURL buildJMXServiceURL(int rmiRegistryPort,
65        int rmiConnectorPort) throws IOException {
66      // Build jmxURL
67      StringBuilder url = new StringBuilder();
68      url.append("service:jmx:rmi://localhost:");
69      url.append(rmiConnectorPort);
70      url.append("/jndi/rmi://localhost:");
71      url.append(rmiRegistryPort);
72      url.append("/jmxrmi");
73  
74      return new JMXServiceURL(url.toString());
75  
76    }
77  
78    public void startConnectorServer(int rmiRegistryPort, int rmiConnectorPort)
79                throws IOException {
80      boolean rmiSSL = false;
81      boolean authenticate = true;
82      String passwordFile = null;
83      String accessFile = null;
84  
85      System.setProperty("java.rmi.server.randomIDs", "true");
86  
87      String rmiSSLValue = System.getProperty("com.sun.management.jmxremote.ssl",
88                                              "false");
89      rmiSSL = Boolean.parseBoolean(rmiSSLValue);
90  
91      String authenticateValue =
92          System.getProperty("com.sun.management.jmxremote.authenticate", "false");
93      authenticate = Boolean.parseBoolean(authenticateValue);
94  
95      passwordFile = System.getProperty("com.sun.management.jmxremote.password.file");
96      accessFile = System.getProperty("com.sun.management.jmxremote.access.file");
97  
98      LOG.info("rmiSSL:" + rmiSSLValue + ",authenticate:" + authenticateValue
99                + ",passwordFile:" + passwordFile + ",accessFile:" + accessFile);
100 
101     // Environment map
102     HashMap<String, Object> jmxEnv = new HashMap<String, Object>();
103 
104     RMIClientSocketFactory csf = null;
105     RMIServerSocketFactory ssf = null;
106 
107     if (rmiSSL) {
108       if (rmiRegistryPort == rmiConnectorPort) {
109         throw new IOException("SSL is enabled. " +
110             "rmiConnectorPort cannot share with the rmiRegistryPort!");
111       }
112       csf = new SslRMIClientSocketFactory();
113       ssf = new SslRMIServerSocketFactory();
114     }
115 
116     if (csf != null) {
117       jmxEnv.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
118     }
119     if (ssf != null) {
120       jmxEnv.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);
121     }
122 
123     // Configure authentication
124     if (authenticate) {
125       jmxEnv.put("jmx.remote.x.password.file", passwordFile);
126       jmxEnv.put("jmx.remote.x.access.file", accessFile);
127     }
128 
129     // Create the RMI registry
130     LocateRegistry.createRegistry(rmiRegistryPort);
131     // Retrieve the PlatformMBeanServer.
132     MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
133 
134     // Build jmxURL
135     JMXServiceURL serviceUrl = buildJMXServiceURL(rmiRegistryPort, rmiConnectorPort);
136 
137     try {
138       // Start the JMXListener with the connection string
139       jmxCS = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, jmxEnv, mbs);
140       jmxCS.start();
141       LOG.info("ConnectorServer started!");
142     } catch (IOException e) {
143       LOG.error("fail to start connector server!", e);
144     }
145 
146   }
147 
148   public void stopConnectorServer() throws IOException {
149     synchronized(JMXListener.class) {
150       if (jmxCS != null) {
151         jmxCS.stop();
152         LOG.info("ConnectorServer stopped!");
153         jmxCS = null;
154       }
155     }
156   }
157 
158 
159   @Override
160   public void start(CoprocessorEnvironment env) throws IOException {
161     int rmiRegistryPort = -1;
162     int rmiConnectorPort = -1;
163     Configuration conf = env.getConfiguration();
164 
165     if (env instanceof MasterCoprocessorEnvironment) {
166       LOG.error("JMXListener should not be loaded in Master Environment!");
167     } else if (env instanceof RegionServerCoprocessorEnvironment) {
168       // running on RegionServer --since 0.99 HMaster is also a HRegionServer
169       rmiRegistryPort =
170         conf.getInt("regionserver" + RMI_REGISTRY_PORT_CONF_KEY, defRMIRegistryPort);
171       rmiConnectorPort =
172         conf.getInt("regionserver" + RMI_CONNECTOR_PORT_CONF_KEY, rmiRegistryPort);
173       LOG.info("RegionServer rmiRegistryPort:" + rmiRegistryPort
174         + ",RegionServer rmiConnectorPort:" + rmiConnectorPort);
175 
176     } else if (env instanceof RegionCoprocessorEnvironment) {
177       LOG.error("JMXListener should not be loaded in Region Environment!");
178     }
179 
180     synchronized(JMXListener.class) {
181       if (jmxCS != null) {
182         LOG.info("JMXListener has been started at Registry port " + rmiRegistryPort);
183       }
184       else {
185         startConnectorServer(rmiRegistryPort, rmiConnectorPort);
186       }
187     }
188   }
189 
190   @Override
191   public void stop(CoprocessorEnvironment env) throws IOException {
192     stopConnectorServer();
193   }
194 
195 }