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 int defMasterRMIRegistryPort = 10101;
55    public static int defRegionserverRMIRegistryPort = 10102;
56  
57    private JMXConnectorServer jmxCS = null;
58  
59    public static JMXServiceURL buildJMXServiceURL(int rmiRegistryPort,
60        int rmiConnectorPort) throws IOException {
61      // Build jmxURL
62      StringBuilder url = new StringBuilder();
63      url.append("service:jmx:rmi://localhost:");
64      url.append(rmiConnectorPort);
65      url.append("/jndi/rmi://localhost:");
66      url.append(rmiRegistryPort);
67      url.append("/jmxrmi");
68  
69      return new JMXServiceURL(url.toString());
70  
71    }
72  
73    public void startConnectorServer(int rmiRegistryPort, int rmiConnectorPort)
74                throws IOException {
75      boolean rmiSSL = false;
76      boolean authenticate = true;
77      String passwordFile = null;
78      String accessFile = null;
79  
80      System.setProperty("java.rmi.server.randomIDs", "true");
81  
82      String rmiSSLValue = System.getProperty("com.sun.management.jmxremote.ssl",
83                                              "false");
84      rmiSSL = Boolean.parseBoolean(rmiSSLValue);
85  
86      String authenticateValue =
87          System.getProperty("com.sun.management.jmxremote.authenticate", "false");
88      authenticate = Boolean.parseBoolean(authenticateValue);
89  
90      passwordFile = System.getProperty("com.sun.management.jmxremote.password.file");
91      accessFile = System.getProperty("com.sun.management.jmxremote.access.file");
92  
93      LOG.info("rmiSSL:" + rmiSSLValue + ",authenticate:" + authenticateValue
94                + ",passwordFile:" + passwordFile + ",accessFile:" + accessFile);
95  
96      // Environment map
97      HashMap<String, Object> jmxEnv = new HashMap<String, Object>();
98  
99      RMIClientSocketFactory csf = null;
100     RMIServerSocketFactory ssf = null;
101 
102     if (rmiSSL) {
103       if (rmiRegistryPort == rmiConnectorPort) {
104         throw new IOException("SSL is enabled. " +
105             "rmiConnectorPort cannot share with the rmiRegistryPort!");
106       }
107       csf = new SslRMIClientSocketFactory();
108       ssf = new SslRMIServerSocketFactory();
109     }
110 
111     if (csf != null) {
112       jmxEnv.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
113     }
114     if (ssf != null) {
115       jmxEnv.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);
116     }
117 
118     // Configure authentication
119     if (authenticate) {
120       jmxEnv.put("jmx.remote.x.password.file", passwordFile);
121       jmxEnv.put("jmx.remote.x.access.file", accessFile);
122     }
123 
124     // Create the RMI registry
125     LocateRegistry.createRegistry(rmiRegistryPort);
126     // Retrieve the PlatformMBeanServer.
127     MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
128 
129     // Build jmxURL
130     JMXServiceURL serviceUrl = buildJMXServiceURL(rmiRegistryPort, rmiConnectorPort);
131 
132     try {
133       // Start the JMXListener with the connection string
134       jmxCS = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, jmxEnv, mbs);
135       jmxCS.start();
136       LOG.info("ConnectorServer started!");
137     } catch (IOException e) {
138       LOG.error("fail to start connector server!", e);
139     }
140 
141   }
142 
143   public void stopConnectorServer() throws IOException {
144     if (jmxCS != null) {
145       jmxCS.stop();
146       LOG.info("ConnectorServer stopped!");
147       jmxCS = null;
148     }
149   }
150 
151 
152   @Override
153   public void start(CoprocessorEnvironment env) throws IOException {
154     int rmiRegistryPort = -1;
155     int rmiConnectorPort = -1;
156     Configuration conf = env.getConfiguration();
157 
158     if (env instanceof MasterCoprocessorEnvironment) {
159       // running on Master
160       rmiRegistryPort =
161         conf.getInt("master" + RMI_REGISTRY_PORT_CONF_KEY,
162         defMasterRMIRegistryPort);
163       rmiConnectorPort =
164         conf.getInt("master" + RMI_CONNECTOR_PORT_CONF_KEY, rmiRegistryPort);
165       LOG.info("Master rmiRegistryPort:" + rmiRegistryPort
166         + ",Master rmiConnectorPort:" + rmiConnectorPort);
167 
168     } else if (env instanceof RegionServerCoprocessorEnvironment) {
169       // running on RegionServer
170       rmiRegistryPort =
171         conf.getInt("regionserver" + RMI_REGISTRY_PORT_CONF_KEY,
172         defRegionserverRMIRegistryPort);
173       rmiConnectorPort =
174         conf.getInt("regionserver" + RMI_CONNECTOR_PORT_CONF_KEY, rmiRegistryPort);
175       LOG.info("RegionServer rmiRegistryPort:" + rmiRegistryPort
176         + ",RegionServer rmiConnectorPort:" + rmiConnectorPort);
177 
178     } else if (env instanceof RegionCoprocessorEnvironment) {
179       LOG.error("JMXListener should not be loaded in Region Environment!");
180     }
181 
182     startConnectorServer(rmiRegistryPort, rmiConnectorPort);
183   }
184 
185   @Override
186   public void stop(CoprocessorEnvironment env) throws IOException {
187     stopConnectorServer();
188   }
189 
190 }