View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.metrics2.impl;
19  
20  import java.util.concurrent.ScheduledFuture;
21  import java.util.concurrent.TimeUnit;
22  import java.util.concurrent.atomic.AtomicBoolean;
23  import java.util.concurrent.atomic.AtomicReference;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.hbase.classification.InterfaceAudience;
28  import org.apache.hadoop.metrics2.MetricsExecutor;
29  import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
30  import org.apache.hadoop.metrics2.lib.MetricsExecutorImpl;
31  import org.apache.hadoop.util.StringUtils;
32  
33  /**
34   * JMX caches the beans that have been exported; even after the values are removed from hadoop's
35   * metrics system the keys and old values will still remain.  This class stops and restarts the
36   * Hadoop metrics system, forcing JMX to clear the cache of exported metrics.
37   *
38   * This class need to be in the o.a.h.metrics2.impl namespace as many of the variables/calls used
39   * are package private.
40   */
41  @InterfaceAudience.Private
42  public class JmxCacheBuster {
43    private static final Log LOG = LogFactory.getLog(JmxCacheBuster.class);
44    private static AtomicReference<ScheduledFuture> fut = new AtomicReference<>(null);
45    private static MetricsExecutor executor = new MetricsExecutorImpl();
46    private static AtomicBoolean stopped = new AtomicBoolean(false);
47  
48    private JmxCacheBuster() {
49      // Static only cache.
50    }
51  
52    /**
53     * For JMX to forget about all previously exported metrics.
54     */
55    public static void clearJmxCache() {
56      if (LOG.isTraceEnabled()) {
57        LOG.trace("clearing JMX Cache" + StringUtils.stringifyException(new Exception()));
58      }
59      //If there are more then 100 ms before the executor will run then everything should be merged.
60      ScheduledFuture future = fut.get();
61      if ((future != null && (!future.isDone() && future.getDelay(TimeUnit.MILLISECONDS) > 100))) {
62        // BAIL OUT
63        return;
64      }
65      if (stopped.get()) {
66        return;
67      }
68      future = executor.getExecutor().schedule(new JmxCacheBusterRunnable(), 5, TimeUnit.SECONDS);
69      fut.set(future);
70    }
71  
72    /**
73     * Stops the clearing of JMX metrics and restarting the Hadoop metrics system. This is needed for
74     * some test environments where we manually inject sources or sinks dynamically.
75     */
76    public static void stop() {
77      stopped.set(true);
78      ScheduledFuture future = fut.get();
79      future.cancel(false);
80    }
81  
82    /**
83     * Restarts the stopped service.
84     * @see #stop()
85     */
86    public static void restart() {
87      stopped.set(false);
88    }
89  
90    final static class JmxCacheBusterRunnable implements Runnable {
91      @Override
92      public void run() {
93        if (LOG.isTraceEnabled()) {
94          LOG.trace("Clearing JMX mbean cache.");
95        }
96  
97        // This is pretty extreme but it's the best way that
98        // I could find to get metrics to be removed.
99        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 }