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 @SuppressWarnings("GetClassOnEnum") 037 Class<? extends DefaultMetricsSystem> clazz = DefaultMetricsSystem.INSTANCE.getClass(); 038 Method m; 039 try { 040 m = clazz.getDeclaredMethod("removeObjectName", String.class); 041 m.setAccessible(true); 042 } catch (NoSuchMethodException e) { 043 m = null; 044 } 045 removeObjectMethod = m; 046 047 Field f1, f2; 048 try { 049 f1 = clazz.getDeclaredField("sourceNames"); 050 f1.setAccessible(true); 051 f2 = UniqueNames.class.getDeclaredField("map"); 052 f2.setAccessible(true); 053 } catch (NoSuchFieldException e) { 054 LOG.trace(e.toString(), e); 055 f1 = null; 056 f2 = null; 057 } 058 sourceNamesField = f1; 059 mapField = f2; 060 } 061 062 public boolean removeObjectName(final String name) { 063 if (removeObjectMethod != null) { 064 try { 065 removeObjectMethod.invoke(DefaultMetricsSystem.INSTANCE, name); 066 return true; 067 } catch (Exception e) { 068 if (LOG.isTraceEnabled()) { 069 LOG.trace("Unable to remove object name from cache: " + name, e); 070 } 071 } 072 } 073 return false; 074 } 075 076 /** 077 * Unfortunately Hadoop tries to be too-clever and permanently keeps track of all names registered 078 * so far as a Source, thus preventing further re-registration of the source with the same name. 079 * In case of dynamic metrics tied to region-lifecycles, this becomes a problem because we would 080 * like to be able to re-register and remove with the same name. Otherwise, it is resource leak. 081 * This ugly code manually removes the name from the UniqueNames map. TODO: May not be needed for 082 * Hadoop versions after YARN-5190. 083 */ 084 public void removeSourceName(String name) { 085 if (sourceNamesField == null || mapField == null) { 086 return; 087 } 088 try { 089 Object sourceNames = sourceNamesField.get(DefaultMetricsSystem.INSTANCE); 090 HashMap map = (HashMap) mapField.get(sourceNames); 091 synchronized (sourceNames) { 092 map.remove(name); 093 } 094 } catch (Exception ex) { 095 if (LOG.isTraceEnabled()) { 096 LOG.trace( 097 "Received exception while trying to access Hadoop Metrics classes via " + "reflection.", 098 ex); 099 } 100 } 101 } 102}