View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.metrics2.lib;
19  
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Method;
22  import java.util.HashMap;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  public class DefaultMetricsSystemHelper {
28  
29    private static final Log LOG = LogFactory.getLog(DefaultMetricsSystemHelper.class);
30    private final Method removeObjectMethod;
31    private final Field sourceNamesField;
32    private final Field mapField;
33  
34    public DefaultMetricsSystemHelper() {
35      Class<? extends DefaultMetricsSystem> clazz = DefaultMetricsSystem.INSTANCE.getClass();
36      Method m;
37      try {
38        m = clazz.getDeclaredMethod("removeObjectName", String.class);
39        m.setAccessible(true);
40      } catch (NoSuchMethodException e) {
41        m = null;
42      }
43      removeObjectMethod = m;
44  
45      Field f1, f2;
46      try {
47        f1 = clazz.getDeclaredField("sourceNames");
48        f1.setAccessible(true);
49        f2 = UniqueNames.class.getDeclaredField("map");
50        f2.setAccessible(true);
51      } catch (NoSuchFieldException e) {
52        LOG.trace(e);
53        f1 = null;
54        f2 = null;
55      }
56      sourceNamesField = f1;
57      mapField = f2;
58    }
59  
60    public boolean removeObjectName(final String name) {
61      if (removeObjectMethod != null) {
62        try {
63          removeObjectMethod.invoke(DefaultMetricsSystem.INSTANCE, name);
64          return true;
65        } catch (Exception e) {
66          if (LOG.isTraceEnabled()) {
67            LOG.trace("Unable to remove object name from cache: " + name, e);
68          }
69        }
70      }
71      return false;
72    }
73  
74    /**
75     * Unfortunately Hadoop tries to be too-clever and permanently keeps track of all names registered
76     * so far as a Source, thus preventing further re-registration of the source with the same name.
77     * In case of dynamic metrics tied to region-lifecycles, this becomes a problem because we would
78     * like to be able to re-register and remove with the same name. Otherwise, it is resource leak.
79     * This ugly code manually removes the name from the UniqueNames map.
80     * TODO: May not be needed for Hadoop versions after YARN-5190.
81     */
82    public void removeSourceName(String name) {
83      if (sourceNamesField == null || mapField == null) {
84        return;
85      }
86      try {
87        Object sourceNames = sourceNamesField.get(DefaultMetricsSystem.INSTANCE);
88        HashMap map = (HashMap) mapField.get(sourceNames);
89        synchronized (sourceNames) {
90          map.remove(name);
91        }
92      } catch (Exception ex) {
93        if (LOG.isTraceEnabled()) {
94          LOG.trace("Received exception while trying to access Hadoop Metrics classes via reflection.",
95              ex);
96        }
97      }
98    }
99  }