001/* 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase; 020 021import java.util.ArrayList; 022import java.util.List; 023 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027/** 028 * Utility class to check the resources: 029 * - log them before and after each test method 030 * - check them against a minimum or maximum 031 * - check that they don't leak during the test 032 */ 033public class ResourceChecker { 034 private static final Logger LOG = LoggerFactory.getLogger(ResourceChecker.class); 035 private String tagLine; 036 037 enum Phase { 038 INITIAL, INTERMEDIATE, END 039 } 040 041 /** 042 * Constructor 043 * @param tagLine The tagLine is added to the logs. Must not be null. 044 */ 045 public ResourceChecker(final String tagLine) { 046 this.tagLine = tagLine; 047 } 048 049 /** 050 * Class to implement for each type of resource. 051 */ 052 abstract static class ResourceAnalyzer { 053 /** 054 * Maximum we set for the resource. Will get a warning in logs 055 * if we go over this limit. 056 */ 057 public int getMax() { 058 return Integer.MAX_VALUE; 059 } 060 061 /** 062 * Minimum we set for the resource. Will get a warning in logs 063 * if we go under this limit. 064 */ 065 public int getMin() { 066 return Integer.MIN_VALUE; 067 } 068 069 /** 070 * Name of the resource analyzed. By default extracted from the class name, but 071 * can be overridden by the subclasses. 072 */ 073 public String getName() { 074 String className = this.getClass().getSimpleName(); 075 final String extName = ResourceAnalyzer.class.getSimpleName(); 076 if (className.endsWith(extName)) { 077 return className.substring(0, className.length() - extName.length()); 078 } else { 079 return className; 080 } 081 } 082 083 /** 084 * The value for the resource. 085 * @param phase the {@link Phase} to get the value for 086 */ 087 abstract public int getVal(Phase phase); 088 089 /* 090 * Retrieves List of Strings which would be logged in logEndings() 091 */ 092 public List<String> getStringsToLog() { 093 return null; 094 } 095 } 096 097 private List<ResourceAnalyzer> ras = new ArrayList<>(); 098 private int[] initialValues; 099 private int[] endingValues; 100 101 private void fillInit() { 102 initialValues = new int[ras.size()]; 103 fill(Phase.INITIAL, initialValues); 104 } 105 106 private void fillEndings() { 107 endingValues = new int[ras.size()]; 108 fill(Phase.END, endingValues); 109 } 110 111 private void fill(Phase phase, int[] vals) { 112 int i = 0; 113 for (ResourceAnalyzer ra : ras) { 114 vals[i++] = ra.getVal(phase); 115 } 116 } 117 118 public void checkInit() { 119 check(initialValues); 120 } 121 122 private void checkEndings() { 123 check(endingValues); 124 } 125 126 private void check(int[] vals) { 127 int i = 0; 128 for (ResourceAnalyzer ra : ras) { 129 int cur = vals[i++]; 130 if (cur < ra.getMin()) { 131 LOG.warn(ra.getName() + "=" + cur + " is inferior to " + ra.getMin()); 132 } 133 if (cur > ra.getMax()) { 134 LOG.warn(ra.getName() + "=" + cur + " is superior to " + ra.getMax()); 135 } 136 } 137 } 138 139 private void logInit() { 140 int i = 0; 141 StringBuilder sb = new StringBuilder(); 142 for (ResourceAnalyzer ra : ras) { 143 int cur = initialValues[i++]; 144 145 if (sb.length() > 0) { 146 sb.append(", "); 147 } 148 149 sb.append(ra.getName()).append("=").append(cur); 150 } 151 LOG.info("before: " + tagLine + " " + sb); 152 } 153 154 private void logEndings() { 155 assert initialValues.length == ras.size(); 156 assert endingValues.length == ras.size(); 157 158 int i = 0; 159 StringBuilder sb = new StringBuilder(); 160 for (ResourceAnalyzer ra : ras) { 161 int curP = initialValues[i]; 162 int curN = endingValues[i++]; 163 164 if (sb.length() > 0) { 165 sb.append(", "); 166 } 167 168 sb.append(ra.getName()).append("=").append(curN).append(" (was ").append(curP).append(")"); 169 if (curN > curP) { 170 List<String> strings = ra.getStringsToLog(); 171 if (strings != null) { 172 for (String s : strings) { 173 sb.append(s); 174 } 175 } 176 sb.append(" - ").append(ra.getName()).append(" LEAK? -"); 177 } 178 } 179 LOG.info("after: " + tagLine + " " + sb); 180 } 181 182 /** 183 * To be called as the beginning of a test method: 184 * - measure the resources 185 * - check vs. the limits. 186 * - logs them. 187 */ 188 public void start() { 189 if (ras.isEmpty()) { 190 LOG.info("No resource analyzer"); 191 return; 192 } 193 fillInit(); 194 logInit(); 195 checkInit(); 196 } 197 198 /** 199 * To be called as the end of a test method: 200 * - measure the resources 201 * - check vs. the limits. 202 * - check vs. the initial state 203 * - logs them. 204 */ 205 public void end() { 206 if (ras.isEmpty()) { 207 LOG.info("No resource analyzer"); 208 return; 209 } 210 if (initialValues == null) { 211 LOG.warn("No initial values"); 212 return; 213 } 214 215 fillEndings(); 216 logEndings(); 217 checkEndings(); 218 } 219 220 /** 221 * Adds a resource analyzer to the resources checked. 222 */ 223 public void addResourceAnalyzer(ResourceAnalyzer ra) { 224 ras.add(ra); 225 } 226}