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  
24  import java.util.*;
25  
26  /**
27   * Utility class to check the resources:
28   *  - log them before and after each test method
29   *  - check them against a minimum or maximum
30   *  - check that they don't leak during the test
31   */
32  public class ResourceChecker {
33    private static final Log LOG = LogFactory.getLog(ResourceChecker.class);
34    private String tagLine;
35  
36    enum Phase {
37      INITIAL, INTERMEDIATE, END
38    }
39  
40    /**
41     * Constructor
42     * @param tagLine - the tagLine is added to the logs. Must be be null.
43     */
44    public ResourceChecker(final String tagLine) {
45      this.tagLine = tagLine;
46    }
47  
48    /**
49     * Class to implement for each type of resource.
50     */
51    abstract static class ResourceAnalyzer {
52      /**
53       * Maximum we set for the resource. Will get a warning in logs
54       * if we go over this limit.
55       */
56      public int getMax() {
57        return Integer.MAX_VALUE;
58      }
59  
60      /**
61       * Minimum we set for the resource. Will get a warning in logs
62       * if we go under this limit.
63       */
64      public int getMin() {
65        return Integer.MIN_VALUE;
66      }
67  
68      /**
69       * Name of the resource analyzed. By default extracted from the class name, but
70       *  can be overridden by the subclasses.
71       */
72      public String getName() {
73        String className = this.getClass().getSimpleName();
74        final String extName = ResourceAnalyzer.class.getSimpleName();
75        if (className.endsWith(extName)) {
76          return className.substring(0, className.length() - extName.length());
77        } else {
78          return className;
79        }
80      }
81  
82      /**
83       * The value for the resource.
84       * @param phase the {@link Phase} to get the value for
85       */
86      abstract public int getVal(Phase phase);
87      
88      /*
89       * Retrieves List of Strings which would be logged in logEndings()
90       */
91      public List<String> getStringsToLog() {
92        return null;
93      }
94    }
95  
96    private List<ResourceAnalyzer> ras = new ArrayList<ResourceAnalyzer>();
97    private int[] initialValues;
98    private int[] endingValues;
99  
100   private void fillInit() {
101     initialValues = new int[ras.size()];
102     fill(Phase.INITIAL, initialValues);
103   }
104 
105   private void fillEndings() {
106     endingValues = new int[ras.size()];
107     fill(Phase.END, endingValues);
108   }
109 
110   private void fill(Phase phase, int[] vals) {
111     int i = 0;
112     for (ResourceAnalyzer ra : ras) {
113       vals[i++] = ra.getVal(phase);
114     }
115   }
116 
117   public void checkInit() {
118     check(initialValues);
119   }
120 
121   private void checkEndings() {
122     check(endingValues);
123   }
124 
125   private void check(int[] vals) {
126     int i = 0;
127     for (ResourceAnalyzer ra : ras) {
128       int cur = vals[i++];
129       if (cur < ra.getMin()) {
130         LOG.warn(ra.getName() + "=" + cur + " is inferior to " + ra.getMin());
131       }
132       if (cur > ra.getMax()) {
133         LOG.warn(ra.getName() + "=" + cur + " is superior to " + ra.getMax());
134       }
135     }
136   }
137 
138   private void logInit() {
139     int i = 0;
140     StringBuilder sb = new StringBuilder();
141     for (ResourceAnalyzer ra : ras) {
142       int cur = initialValues[i++];
143 
144       if (sb.length() > 0) {
145         sb.append(", ");
146       }
147 
148       sb.append(ra.getName()).append("=").append(cur);
149     }
150     LOG.info("before: " + tagLine + " " + sb);
151   }
152 
153   private void logEndings() {
154     assert initialValues.length == ras.size();
155     assert endingValues.length == ras.size();
156 
157     int i = 0;
158     StringBuilder sb = new StringBuilder();
159     for (ResourceAnalyzer ra : ras) {
160       int curP = initialValues[i];
161       int curN = endingValues[i++];
162 
163       if (sb.length() > 0) {
164         sb.append(", ");
165       }
166 
167       sb.append(ra.getName()).append("=").append(curN).append(" (was ").append(curP).append(")");
168       if (curN > curP) {
169         List<String> strings = ra.getStringsToLog();
170         if (strings != null) {
171           for (String s : strings) {
172             sb.append(s);
173           }
174         }
175         sb.append(" - ").append(ra.getName()).append(" LEAK? -");
176       }
177     }
178     LOG.info("after: " + tagLine + " " + sb);
179   }
180 
181   /**
182    * To be called as the beginning of a test method:
183    * - measure the resources
184    * - check vs. the limits.
185    * - logs them.
186    */
187   public void start() {
188     if (ras.size() == 0) {
189       LOG.info("No resource analyzer");
190       return;
191     }
192     fillInit();
193     logInit();
194     checkInit();
195   }
196 
197   /**
198    * To be called as the end of a test method:
199    * - measure the resources
200    * - check vs. the limits.
201    * - check vs. the initial state
202    * - logs them.
203    */
204   public void end() {
205     if (ras.size() == 0) {
206       LOG.info("No resource analyzer");
207       return;
208     }
209     if (initialValues == null) {
210       LOG.warn("No initial values");
211       return;
212     }
213 
214     fillEndings();
215     logEndings();
216     checkEndings();
217   }
218 
219   /**
220    * Adds a resource analyzer to the resources checked.
221    */
222   public void addResourceAnalyzer(ResourceAnalyzer ra) {
223     ras.add(ra);
224   }
225 }