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.registry.Registry;
31  import java.rmi.server.RMIClientSocketFactory;
32  import java.rmi.server.RMIServerSocketFactory;
33  import java.rmi.server.UnicastRemoteObject;
34  import java.util.HashMap;
35  
36  import javax.management.MBeanServer;
37  import javax.management.remote.JMXConnectorServer;
38  import javax.management.remote.JMXConnectorServerFactory;
39  import javax.management.remote.JMXServiceURL;
40  import javax.management.remote.rmi.RMIConnectorServer;
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    private 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 defMasterRMIRegistryPort = 10101;
55    public static final int defRegionserverRMIRegistryPort = 10102;
56  
57    /**
58     * workaround for HBASE-11146
59     * master and regionserver are in 1 JVM in standalone mode
60     * only 1 JMX instance is allowed, otherwise there is port conflict even if
61     * we only load regionserver coprocessor on master
62     */
63    private static JMXConnectorServer JMX_CS = null;
64    private Registry rmiRegistry = null;
65  
66    public static JMXServiceURL buildJMXServiceURL(int rmiRegistryPort,
67        int rmiConnectorPort) throws IOException {
68      // Build jmxURL
69      StringBuilder url = new StringBuilder();
70      url.append("service:jmx:rmi://localhost:");
71      url.append(rmiConnectorPort);
72      url.append("/jndi/rmi://localhost:");
73      url.append(rmiRegistryPort);
74      url.append("/jmxrmi");
75  
76      return new JMXServiceURL(url.toString());
77  
78    }
79  
80    public void startConnectorServer(int rmiRegistryPort, int rmiConnectorPort)
81                throws IOException {
82      boolean rmiSSL = false;
83      boolean authenticate = true;
84      String passwordFile = null;
85      String accessFile = null;
86  
87      System.setProperty("java.rmi.server.randomIDs", "true");
88  
89      String rmiSSLValue = System.getProperty("com.sun.management.jmxremote.ssl",
90                                              "false");
91      rmiSSL = Boolean.parseBoolean(rmiSSLValue);
92  
93      String authenticateValue =
94          System.getProperty("com.sun.management.jmxremote.authenticate", "false");
95      authenticate = Boolean.parseBoolean(authenticateValue);
96  
97      passwordFile = System.getProperty("com.sun.management.jmxremote.password.file");
98      accessFile = System.getProperty("com.sun.management.jmxremote.access.file");
99  
100     LOG.info("rmiSSL:" + rmiSSLValue + ",authenticate:" + authenticateValue
101               + ",passwordFile:" + passwordFile + ",accessFile:" + accessFile);
102 
103     // Environment map
104     HashMap<String, Object> jmxEnv = new HashMap<String, Object>();
105 
106     RMIClientSocketFactory csf = null;
107     RMIServerSocketFactory ssf = null;
108 
109     if (rmiSSL) {
110       if (rmiRegistryPort == rmiConnectorPort) {
111         throw new IOException("SSL is enabled. " +
112             "rmiConnectorPort cannot share with the rmiRegistryPort!");
113       }
114       csf = new SslRMIClientSocketFactorySecure();
115       ssf = new SslRMIServerSocketFactorySecure();
116     }
117 
118     if (csf != null) {
119       jmxEnv.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf);
120     }
121     if (ssf != null) {
122       jmxEnv.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);
123     }
124 
125     // Configure authentication
126     if (authenticate) {
127       jmxEnv.put("jmx.remote.x.password.file", passwordFile);
128       jmxEnv.put("jmx.remote.x.access.file", accessFile);
129     }
130 
131     // Create the RMI registry
132     rmiRegistry = LocateRegistry.createRegistry(rmiRegistryPort);
133     // Retrieve the PlatformMBeanServer.
134     MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
135 
136     // Build jmxURL
137     JMXServiceURL serviceUrl = buildJMXServiceURL(rmiRegistryPort, rmiConnectorPort);
138 
139     try {
140       // Start the JMXListener with the connection string
141       synchronized(JMXListener.class) {
142         if (JMX_CS != null) {
143           throw new RuntimeException("Started by another thread?");
144         }
145         JMX_CS = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl, jmxEnv, mbs);
146         JMX_CS.start();
147       }
148       LOG.info("ConnectorServer started!");
149     } catch (IOException e) {
150       LOG.error("fail to start connector server!", e);
151       // deregister the RMI registry
152       if (rmiRegistry != null) {
153         UnicastRemoteObject.unexportObject(rmiRegistry, true);
154       }
155     }
156 
157   }
158 
159   public void stopConnectorServer() throws IOException {
160     synchronized (JMXListener.class) {
161       if (JMX_CS != null) {
162         JMX_CS.stop();
163         LOG.info("ConnectorServer stopped!");
164         JMX_CS = null;
165       }
166       // deregister the RMI registry
167       if (rmiRegistry != null) {
168         UnicastRemoteObject.unexportObject(rmiRegistry, true);
169       }
170     }
171   }
172 
173 
174   @Override
175   public void start(CoprocessorEnvironment env) throws IOException {
176     int rmiRegistryPort = -1;
177     int rmiConnectorPort = -1;
178     Configuration conf = env.getConfiguration();
179 
180     if (env instanceof MasterCoprocessorEnvironment) {
181       // running on Master
182       rmiRegistryPort =
183           conf.getInt("master" + RMI_REGISTRY_PORT_CONF_KEY, defMasterRMIRegistryPort);
184       rmiConnectorPort = conf.getInt("master" + RMI_CONNECTOR_PORT_CONF_KEY, rmiRegistryPort);
185       LOG.info("Master rmiRegistryPort:" + rmiRegistryPort + ",Master rmiConnectorPort:"
186           + rmiConnectorPort);
187     } else if (env instanceof RegionServerCoprocessorEnvironment) {
188       // running on RegionServer
189       rmiRegistryPort =
190         conf.getInt("regionserver" + RMI_REGISTRY_PORT_CONF_KEY,
191         defRegionserverRMIRegistryPort);
192       rmiConnectorPort =
193         conf.getInt("regionserver" + RMI_CONNECTOR_PORT_CONF_KEY, rmiRegistryPort);
194       LOG.info("RegionServer rmiRegistryPort:" + rmiRegistryPort
195         + ",RegionServer rmiConnectorPort:" + rmiConnectorPort);
196 
197     } else if (env instanceof RegionCoprocessorEnvironment) {
198       LOG.error("JMXListener should not be loaded in Region Environment!");
199       return;
200     }
201 
202     synchronized(JMXListener.class) {
203       if (JMX_CS != null) {
204         LOG.info("JMXListener has been started at Registry port " + rmiRegistryPort);
205       }
206       else {
207         startConnectorServer(rmiRegistryPort, rmiConnectorPort);
208       }
209     }
210   }
211 
212   @Override
213   public void stop(CoprocessorEnvironment env) throws IOException {
214     stopConnectorServer();
215   }
216 
217 }