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