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.http.jmx; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.net.HttpURLConnection; 025import java.net.URL; 026import java.net.URLEncoder; 027import java.util.regex.Matcher; 028import java.util.regex.Pattern; 029import javax.servlet.http.HttpServletResponse; 030import org.apache.hadoop.hbase.http.HttpServer; 031import org.apache.hadoop.hbase.http.HttpServerFunctionalTest; 032import org.apache.hadoop.hbase.testclassification.MiscTests; 033import org.apache.hadoop.hbase.testclassification.SmallTests; 034import org.junit.jupiter.api.AfterAll; 035import org.junit.jupiter.api.BeforeAll; 036import org.junit.jupiter.api.Tag; 037import org.junit.jupiter.api.Test; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041@Tag(MiscTests.TAG) 042@Tag(SmallTests.TAG) 043public class TestJMXJsonServlet extends HttpServerFunctionalTest { 044 045 private static final Logger LOG = LoggerFactory.getLogger(TestJMXJsonServlet.class); 046 private static HttpServer server; 047 private static URL baseUrl; 048 049 @BeforeAll 050 public static void setup() throws Exception { 051 // Eclipse doesn't pick this up correctly from the plugin 052 // configuration in the pom. 053 System.setProperty(HttpServerFunctionalTest.TEST_BUILD_WEBAPPS, "target/test-classes/webapps"); 054 server = createTestServer(); 055 server.start(); 056 baseUrl = getServerURL(server); 057 } 058 059 @AfterAll 060 public static void cleanup() throws Exception { 061 server.stop(); 062 } 063 064 public static void assertReFind(String re, String value) { 065 Pattern p = Pattern.compile(re); 066 Matcher m = p.matcher(value); 067 assertTrue(m.find(), "'" + p + "' does not match " + value); 068 } 069 070 public static void assertNotFind(String re, String value) { 071 Pattern p = Pattern.compile(re); 072 Matcher m = p.matcher(value); 073 assertFalse(m.find(), "'" + p + "' should not match " + value); 074 } 075 076 @Test 077 public void testQuery() throws Exception { 078 String result = readOutput(new URL(baseUrl, "/jmx?qry=java.lang:type=Runtime")); 079 LOG.info("/jmx?qry=java.lang:type=Runtime RESULT: " + result); 080 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Runtime\"", result); 081 assertReFind("\"modelerType\"", result); 082 083 result = readOutput(new URL(baseUrl, "/jmx?qry=java.lang:type=Memory")); 084 LOG.info("/jmx?qry=java.lang:type=Memory RESULT: " + result); 085 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result); 086 assertReFind("\"modelerType\"", result); 087 088 result = readOutput(new URL(baseUrl, "/jmx")); 089 LOG.info("/jmx RESULT: " + result); 090 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result); 091 092 // test to get an attribute of a mbean 093 result = readOutput(new URL(baseUrl, "/jmx?get=java.lang:type=Memory::HeapMemoryUsage")); 094 LOG.info("/jmx RESULT: " + result); 095 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result); 096 assertReFind("\"committed\"\\s*:", result); 097 098 // negative test to get an attribute of a mbean 099 result = readOutput(new URL(baseUrl, "/jmx?get=java.lang:type=Memory::")); 100 LOG.info("/jmx RESULT: " + result); 101 assertReFind("\"ERROR\"", result); 102 103 // test to get JSONP result 104 result = readOutput(new URL(baseUrl, "/jmx?qry=java.lang:type=Memory&callback=mycallback1")); 105 LOG.info("/jmx?qry=java.lang:type=Memory&callback=mycallback RESULT: " + result); 106 assertReFind("^mycallback1\\(\\{", result); 107 assertReFind("\\}\\);$", result); 108 109 // negative test to get an attribute of a mbean as JSONP 110 result = readOutput(new URL(baseUrl, "/jmx?get=java.lang:type=Memory::&callback=mycallback2")); 111 LOG.info("/jmx RESULT: " + result); 112 assertReFind("^mycallback2\\(\\{", result); 113 assertReFind("\"ERROR\"", result); 114 assertReFind("\\}\\);$", result); 115 116 // test to get an attribute of a mbean as JSONP 117 result = readOutput( 118 new URL(baseUrl, "/jmx?get=java.lang:type=Memory::HeapMemoryUsage&callback=mycallback3")); 119 LOG.info("/jmx RESULT: " + result); 120 assertReFind("^mycallback3\\(\\{", result); 121 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result); 122 assertReFind("\"committed\"\\s*:", result); 123 assertReFind("\\}\\);$", result); 124 125 // test exclude the specific mbean 126 result = 127 readOutput(new URL(baseUrl, "/jmx?excl=Hadoop:service=HBase,name=RegionServer,sub=Regions")); 128 LOG.info("/jmx RESULT: " + result); 129 assertNotFind("\"name\"\\s*:\\s*\"Hadoop:service=HBase,name=RegionServer,sub=Regions\"", 130 result); 131 } 132 133 @Test 134 public void testGetPattern() throws Exception { 135 // test to get an attribute of a mbean as JSONP 136 String result = 137 readOutput(new URL(baseUrl, "/jmx?get=java.lang:type=Memory::[a-zA-z_]*NonHeapMemoryUsage")); 138 LOG.info("/jmx RESULT: " + result); 139 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result); 140 assertReFind("\"committed\"\\s*:", result); 141 assertReFind("\"NonHeapMemoryUsage\"\\s*:", result); 142 assertNotFind("\"HeapMemoryUsage\"\\s*:", result); 143 144 result = readOutput(new URL(baseUrl, "/jmx?get=java.lang:type=Memory::[^Non]*HeapMemoryUsage")); 145 LOG.info("/jmx RESULT: " + result); 146 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result); 147 assertReFind("\"committed\"\\s*:", result); 148 assertReFind("\"HeapMemoryUsage\"\\s*:", result); 149 assertNotFind("\"NonHeapHeapMemoryUsage\"\\s*:", result); 150 151 result = readOutput(new URL(baseUrl, 152 "/jmx?get=java.lang:type=Memory::[a-zA-z_]*HeapMemoryUsage,[a-zA-z_]*NonHeapMemoryUsage")); 153 LOG.info("/jmx RESULT: " + result); 154 assertReFind("\"name\"\\s*:\\s*\"java.lang:type=Memory\"", result); 155 assertReFind("\"committed\"\\s*:", result); 156 } 157 158 @Test 159 public void testPatternMatching() throws Exception { 160 assertReFind("[a-zA-z_]*Table1[a-zA-z_]*memStoreSize", 161 "Namespace_default_table_Table1_metric_memStoreSize"); 162 assertReFind("[a-zA-z_]*memStoreSize", "Namespace_default_table_Table1_metric_memStoreSize"); 163 } 164 165 @Test 166 public void testDisallowedJSONPCallback() throws Exception { 167 String callback = "function(){alert('bigproblems!')};foo"; 168 URL url = new URL(baseUrl, 169 "/jmx?qry=java.lang:type=Memory&callback=" + URLEncoder.encode(callback, "UTF-8")); 170 HttpURLConnection cnxn = (HttpURLConnection) url.openConnection(); 171 assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, cnxn.getResponseCode()); 172 } 173 174 @Test 175 public void testUnderscoresInJSONPCallback() throws Exception { 176 String callback = "my_function"; 177 URL url = new URL(baseUrl, 178 "/jmx?qry=java.lang:type=Memory&callback=" + URLEncoder.encode(callback, "UTF-8")); 179 HttpURLConnection cnxn = (HttpURLConnection) url.openConnection(); 180 assertEquals(HttpServletResponse.SC_OK, cnxn.getResponseCode()); 181 } 182}