/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; /** * Utility class to check the resources: * - log them before and after each test method * - check them against a minimum or maximum * - check that they don't leak during the test */ public class ResourceChecker { private static final Log LOG = LogFactory.getLog(ResourceChecker.class); private String tagLine; enum Phase { INITIAL, INTERMEDIATE, END } /** * Constructor * @param tagLine - the tagLine is added to the logs. Must be be null. */ public ResourceChecker(final String tagLine) { this.tagLine = tagLine; } /** * Class to implement for each type of resource. */ abstract static class ResourceAnalyzer { /** * Maximum we set for the resource. Will get a warning in logs * if we go over this limit. */ public int getMax() { return Integer.MAX_VALUE; } /** * Minimum we set for the resource. Will get a warning in logs * if we go under this limit. */ public int getMin() { return Integer.MIN_VALUE; } /** * Name of the resource analyzed. By default extracted from the class name, but * can be overridden by the subclasses. */ public String getName() { String className = this.getClass().getSimpleName(); final String extName = ResourceAnalyzer.class.getSimpleName(); if (className.endsWith(extName)) { return className.substring(0, className.length() - extName.length()); } else { return className; } } /** * The value for the resource. * @param phase */ abstract public int getVal(Phase phase); /* * Retrieves List of Strings which would be logged in logEndings() */ public List<String> getStringsToLog() { return null; } } private List<ResourceAnalyzer> ras = new ArrayList<ResourceAnalyzer>(); private int[] initialValues; private int[] endingValues; private void fillInit() { initialValues = new int[ras.size()]; fill(Phase.INITIAL, initialValues); } private void fillEndings() { endingValues = new int[ras.size()]; fill(Phase.END, endingValues); } private void fill(Phase phase, int[] vals) { int i = 0; for (ResourceAnalyzer ra : ras) { vals[i++] = ra.getVal(phase); } } public void checkInit() { check(initialValues); } private void checkEndings() { check(endingValues); } private void check(int[] vals) { int i = 0; for (ResourceAnalyzer ra : ras) { int cur = vals[i++]; if (cur < ra.getMin()) { LOG.warn(ra.getName() + "=" + cur + " is inferior to " + ra.getMin()); } if (cur > ra.getMax()) { LOG.warn(ra.getName() + "=" + cur + " is superior to " + ra.getMax()); } } } private void logInit() { int i = 0; StringBuilder sb = new StringBuilder(); for (ResourceAnalyzer ra : ras) { int cur = initialValues[i++]; if (sb.length() > 0) sb.append(", "); sb.append(ra.getName()).append("=").append(cur); } LOG.info("before: " + tagLine + " " + sb); } private void logEndings() { assert initialValues.length == ras.size(); assert endingValues.length == ras.size(); int i = 0; StringBuilder sb = new StringBuilder(); for (ResourceAnalyzer ra : ras) { int curP = initialValues[i]; int curN = endingValues[i++]; if (sb.length() > 0) sb.append(", "); sb.append(ra.getName()).append("=").append(curN).append(" (was ").append(curP).append(")"); if (curN > curP) { List<String> strings = ra.getStringsToLog(); if (strings != null) { for (String s : strings) { sb.append(s); } } sb.append(" - ").append(ra.getName()).append(" LEAK? -"); } } LOG.info("after: " + tagLine + " " + sb); } /** * To be called as the beginning of a test method: * - measure the resources * - check vs. the limits. * - logs them. */ public void start() { if (ras.size() == 0) { LOG.info("No resource analyzer"); return; } fillInit(); logInit(); checkInit(); } /** * To be called as the end of a test method: * - measure the resources * - check vs. the limits. * - check vs. the initial state * - logs them. */ public void end() { if (ras.size() == 0) { LOG.info("No resource analyzer"); return; } if (initialValues == null) { LOG.warn("No initial values"); return; } fillEndings(); logEndings(); checkEndings(); } /** * Adds a resource analyzer to the resources checked. */ public void addResourceAnalyzer(ResourceAnalyzer ra) { ras.add(ra); } }