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