001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.metrics2.lib; 019 020import java.lang.reflect.Field; 021import java.lang.reflect.Method; 022import java.util.HashMap; 023import org.apache.yetus.audience.InterfaceAudience; 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027@InterfaceAudience.Private 028public class DefaultMetricsSystemHelper { 029 030 private static final Logger LOG = LoggerFactory.getLogger(DefaultMetricsSystemHelper.class); 031 private final Method removeObjectMethod; 032 private final Field sourceNamesField; 033 private final Field mapField; 034 035 public DefaultMetricsSystemHelper() { 036 Class<? extends DefaultMetricsSystem> clazz = DefaultMetricsSystem.INSTANCE.getClass(); 037 Method m; 038 try { 039 m = clazz.getDeclaredMethod("removeObjectName", String.class); 040 m.setAccessible(true); 041 } catch (NoSuchMethodException e) { 042 m = null; 043 } 044 removeObjectMethod = m; 045 046 Field f1, f2; 047 try { 048 f1 = clazz.getDeclaredField("sourceNames"); 049 f1.setAccessible(true); 050 f2 = UniqueNames.class.getDeclaredField("map"); 051 f2.setAccessible(true); 052 } catch (NoSuchFieldException e) { 053 LOG.trace(e.toString(), e); 054 f1 = null; 055 f2 = null; 056 } 057 sourceNamesField = f1; 058 mapField = f2; 059 } 060 061 public boolean removeObjectName(final String name) { 062 if (removeObjectMethod != null) { 063 try { 064 removeObjectMethod.invoke(DefaultMetricsSystem.INSTANCE, name); 065 return true; 066 } catch (Exception e) { 067 if (LOG.isTraceEnabled()) { 068 LOG.trace("Unable to remove object name from cache: " + name, e); 069 } 070 } 071 } 072 return false; 073 } 074 075 /** 076 * Unfortunately Hadoop tries to be too-clever and permanently keeps track of all names registered 077 * so far as a Source, thus preventing further re-registration of the source with the same name. 078 * In case of dynamic metrics tied to region-lifecycles, this becomes a problem because we would 079 * like to be able to re-register and remove with the same name. Otherwise, it is resource leak. 080 * This ugly code manually removes the name from the UniqueNames map. 081 * TODO: May not be needed for Hadoop versions after YARN-5190. 082 */ 083 public void removeSourceName(String name) { 084 if (sourceNamesField == null || mapField == null) { 085 return; 086 } 087 try { 088 Object sourceNames = sourceNamesField.get(DefaultMetricsSystem.INSTANCE); 089 HashMap map = (HashMap) mapField.get(sourceNames); 090 synchronized (sourceNames) { 091 map.remove(name); 092 } 093 } catch (Exception ex) { 094 if (LOG.isTraceEnabled()) { 095 LOG.trace("Received exception while trying to access Hadoop Metrics classes via " + 096 "reflection.", ex); 097 } 098 } 099 } 100}