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