001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.io.IOException; 021import javax.management.remote.JMXConnector; 022import javax.management.remote.JMXConnectorFactory; 023import javax.naming.ServiceUnavailableException; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.client.Admin; 026import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 027import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 028import org.apache.hadoop.hbase.coprocessor.ObserverContext; 029import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; 030import org.apache.hadoop.hbase.security.AccessDeniedException; 031import org.apache.hadoop.hbase.security.access.AccessController; 032import org.apache.hadoop.hbase.testclassification.MediumTests; 033import org.apache.hadoop.hbase.testclassification.MiscTests; 034import org.junit.After; 035import org.junit.Assert; 036import org.junit.Before; 037import org.junit.ClassRule; 038import org.junit.Test; 039import org.junit.experimental.categories.Category; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043/** 044 * Test case for JMX Connector Server. 045 */ 046@Category({ MiscTests.class, MediumTests.class }) 047public class TestJMXConnectorServer { 048 049 @ClassRule 050 public static final HBaseClassTestRule CLASS_RULE = 051 HBaseClassTestRule.forClass(TestJMXConnectorServer.class); 052 053 private static final Logger LOG = LoggerFactory.getLogger(TestJMXConnectorServer.class); 054 private static HBaseTestingUtility UTIL = new HBaseTestingUtility(); 055 056 private static Configuration conf = null; 057 private static Admin admin; 058 // RMI registry port 059 private static int rmiRegistryPort = 61120; 060 // Switch for customized Accesscontroller to throw ACD exception while executing test case 061 static boolean hasAccess; 062 063 @Before 064 public void setUp() throws Exception { 065 UTIL = new HBaseTestingUtility(); 066 conf = UTIL.getConfiguration(); 067 } 068 069 @After 070 public void tearDown() throws Exception { 071 // Set to true while stopping cluster 072 hasAccess = true; 073 admin.close(); 074 UTIL.shutdownMiniCluster(); 075 } 076 077 /** 078 * This tests to validate the HMaster's ConnectorServer after unauthorised stopMaster call. 079 */ 080 @Test 081 public void testHMConnectorServerWhenStopMaster() throws Exception { 082 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, 083 JMXListener.class.getName() + "," + MyAccessController.class.getName()); 084 conf.setInt("master.rmi.registry.port", rmiRegistryPort); 085 UTIL.startMiniCluster(); 086 admin = UTIL.getConnection().getAdmin(); 087 088 // try to stop master 089 boolean accessDenied = false; 090 try { 091 hasAccess = false; 092 LOG.info("Stopping HMaster..."); 093 admin.stopMaster(); 094 } catch (AccessDeniedException e) { 095 LOG.info("Exception occurred while stopping HMaster. ", e); 096 accessDenied = true; 097 } 098 Assert.assertTrue(accessDenied); 099 100 // Check whether HMaster JMX Connector server can be connected 101 JMXConnector connector = null; 102 try { 103 connector = JMXConnectorFactory 104 .connect(JMXListener.buildJMXServiceURL(rmiRegistryPort, rmiRegistryPort)); 105 } catch (IOException e) { 106 if (e.getCause() instanceof ServiceUnavailableException) { 107 Assert.fail("Can't connect to HMaster ConnectorServer."); 108 } 109 } 110 Assert.assertNotNull("JMXConnector should not be null.", connector); 111 connector.close(); 112 } 113 114 /** 115 * This tests to validate the RegionServer's ConnectorServer after unauthorised stopRegionServer 116 * call. 117 */ 118 @Test 119 public void testRSConnectorServerWhenStopRegionServer() throws Exception { 120 conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, 121 JMXListener.class.getName() + "," + MyAccessController.class.getName()); 122 conf.setInt("regionserver.rmi.registry.port", rmiRegistryPort); 123 UTIL.startMiniCluster(); 124 admin = UTIL.getConnection().getAdmin(); 125 126 hasAccess = false; 127 ServerName serverName = UTIL.getHBaseCluster().getRegionServer(0).getServerName(); 128 LOG.info("Stopping Region Server..."); 129 admin.stopRegionServer(serverName.getHostname() + ":" + serverName.getPort()); 130 131 // Check whether Region Sever JMX Connector server can be connected 132 JMXConnector connector = null; 133 try { 134 connector = JMXConnectorFactory 135 .connect(JMXListener.buildJMXServiceURL(rmiRegistryPort, rmiRegistryPort)); 136 } catch (IOException e) { 137 if (e.getCause() instanceof ServiceUnavailableException) { 138 Assert.fail("Can't connect to Region Server ConnectorServer."); 139 } 140 } 141 Assert.assertNotNull("JMXConnector should not be null.", connector); 142 connector.close(); 143 } 144 145 /** 146 * This tests to validate the HMaster's ConnectorServer after unauthorised shutdown call. 147 */ 148 @Test 149 public void testHMConnectorServerWhenShutdownCluster() throws Exception { 150 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, 151 JMXListener.class.getName() + "," + MyAccessController.class.getName()); 152 conf.setInt("master.rmi.registry.port", rmiRegistryPort); 153 154 UTIL.startMiniCluster(); 155 admin = UTIL.getConnection().getAdmin(); 156 157 boolean accessDenied = false; 158 try { 159 hasAccess = false; 160 LOG.info("Stopping HMaster..."); 161 admin.shutdown(); 162 } catch (AccessDeniedException e) { 163 LOG.error("Exception occurred while stopping HMaster. ", e); 164 accessDenied = true; 165 } 166 Assert.assertTrue(accessDenied); 167 168 // Check whether HMaster JMX Connector server can be connected 169 JMXConnector connector = null; 170 try { 171 connector = JMXConnectorFactory 172 .connect(JMXListener.buildJMXServiceURL(rmiRegistryPort, rmiRegistryPort)); 173 } catch (IOException e) { 174 if (e.getCause() instanceof ServiceUnavailableException) { 175 Assert.fail("Can't connect to HMaster ConnectorServer."); 176 } 177 } 178 Assert.assertNotNull("JMXConnector should not be null.", connector); 179 connector.close(); 180 } 181 182 /* 183 * Customized class for test case execution which will throw ACD exception while executing 184 * stopMaster/preStopRegionServer/preShutdown explicitly. 185 */ 186 public static class MyAccessController extends AccessController { 187 @Override 188 public void postStartMaster(ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException { 189 // Do nothing. In particular, stop the creation of the hbase:acl table. It makes the 190 // shutdown take time. 191 } 192 193 @Override 194 public void preStopMaster(ObserverContext<MasterCoprocessorEnvironment> c) throws IOException { 195 if (!hasAccess) { 196 throw new AccessDeniedException("Insufficient permissions to stop master"); 197 } 198 } 199 200 @Override 201 public void preStopRegionServer(ObserverContext<RegionServerCoprocessorEnvironment> ctx) 202 throws IOException { 203 if (!hasAccess) { 204 throw new AccessDeniedException("Insufficient permissions to stop region server."); 205 } 206 } 207 208 @Override 209 public void preShutdown(ObserverContext<MasterCoprocessorEnvironment> c) throws IOException { 210 if (!hasAccess) { 211 throw new AccessDeniedException("Insufficient permissions to shut down cluster."); 212 } 213 } 214 } 215}