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.impl; 019 020import java.util.concurrent.ScheduledFuture; 021import java.util.concurrent.TimeUnit; 022import java.util.concurrent.atomic.AtomicBoolean; 023import java.util.concurrent.atomic.AtomicReference; 024import org.apache.hadoop.metrics2.MetricsExecutor; 025import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; 026import org.apache.hadoop.metrics2.lib.MetricsExecutorImpl; 027import org.apache.hadoop.util.StringUtils; 028import org.apache.yetus.audience.InterfaceAudience; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032/** 033 * JMX caches the beans that have been exported; even after the values are removed from hadoop's 034 * metrics system the keys and old values will still remain. This class stops and restarts the 035 * Hadoop metrics system, forcing JMX to clear the cache of exported metrics. This class need to be 036 * in the o.a.h.metrics2.impl namespace as many of the variables/calls used are package private. 037 */ 038@InterfaceAudience.Private 039public final class JmxCacheBuster { 040 private static final Logger LOG = LoggerFactory.getLogger(JmxCacheBuster.class); 041 private static final AtomicReference<ScheduledFuture<?>> FUT = new AtomicReference<>(null); 042 private static final MetricsExecutor EXECUTOR = new MetricsExecutorImpl(); 043 private static final AtomicBoolean STOPPED = new AtomicBoolean(false); 044 045 private JmxCacheBuster() { 046 // Static only cache. 047 } 048 049 /** 050 * For JMX to forget about all previously exported metrics. 051 */ 052 public static void clearJmxCache() { 053 if (LOG.isTraceEnabled()) { 054 LOG.trace("clearing JMX Cache" + StringUtils.stringifyException(new Exception())); 055 } 056 // If there are more then 100 ms before the executor will run then everything should be merged. 057 ScheduledFuture<?> future = FUT.get(); 058 if ((future != null && (!future.isDone() && future.getDelay(TimeUnit.MILLISECONDS) > 100))) { 059 // BAIL OUT 060 return; 061 } 062 if (STOPPED.get()) { 063 return; 064 } 065 future = EXECUTOR.getExecutor().schedule(new JmxCacheBusterRunnable(), 5, TimeUnit.SECONDS); 066 FUT.set(future); 067 } 068 069 /** 070 * Stops the clearing of JMX metrics and restarting the Hadoop metrics system. This is needed for 071 * some test environments where we manually inject sources or sinks dynamically. 072 */ 073 public static void stop() { 074 STOPPED.set(true); 075 ScheduledFuture<?> future = FUT.get(); 076 if (future != null) { 077 future.cancel(false); 078 } 079 } 080 081 /** 082 * Restarts the stopped service. 083 * @see #stop() 084 */ 085 public static void restart() { 086 STOPPED.set(false); 087 } 088 089 final static class JmxCacheBusterRunnable implements Runnable { 090 @Override 091 public void run() { 092 if (LOG.isTraceEnabled()) { 093 LOG.trace("Clearing JMX mbean cache."); 094 } 095 096 // This is pretty extreme but it's the best way that 097 // I could find to get metrics to be removed. 098 try { 099 if (DefaultMetricsSystem.instance() != null) { 100 DefaultMetricsSystem.instance().stop(); 101 // Sleep some time so that the rest of the hadoop metrics 102 // system knows that things are done 103 Thread.sleep(500); 104 DefaultMetricsSystem.instance().start(); 105 } 106 } catch (Exception exception) { 107 LOG.debug("error clearing the jmx it appears the metrics system hasn't been started", 108 exception); 109 } 110 } 111 } 112}