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  import com.google.common.annotations.VisibleForTesting;
34
35  /**
36   * JMX caches the beans that have been exported; even after the values are removed from hadoop's
37   * metrics system the keys and old values will still remain.  This class stops and restarts the
38   * Hadoop metrics system, forcing JMX to clear the cache of exported metrics.
39   *
40   * This class need to be in the o.a.h.metrics2.impl namespace as many of the variables/calls used
41   * are package private.
42   */
43  @InterfaceAudience.Private
44  public class JmxCacheBuster {
45    private static final Log LOG = LogFactory.getLog(JmxCacheBuster.class);
46    private static AtomicReference<ScheduledFuture> fut = new AtomicReference<>(null);
47    private static MetricsExecutor executor = new MetricsExecutorImpl();
48    private static AtomicBoolean stopped = new AtomicBoolean(false);
49
50    private JmxCacheBuster() {
51      // Static only cache.
52    }
53
54    /**
55     * For JMX to forget about all previously exported metrics.
56     */
57    public static void clearJmxCache() {
58      if (LOG.isTraceEnabled()) {
59        LOG.trace("clearing JMX Cache" + StringUtils.stringifyException(new Exception()));
60      }
61      //If there are more then 100 ms before the executor will run then everything should be merged.
62      ScheduledFuture future = fut.get();
63      if ((future != null && (!future.isDone() && future.getDelay(TimeUnit.MILLISECONDS) > 100))) {
64        // BAIL OUT
65        return;
66      }
67      if (stopped.get()) {
68        return;
69      }
70      future = executor.getExecutor().schedule(new JmxCacheBusterRunnable(), 5, TimeUnit.SECONDS);
71      fut.set(future);
72    }
73
74    /**
75     * Stops the clearing of JMX metrics and restarting the Hadoop metrics system. This is needed for
76     * some test environments where we manually inject sources or sinks dynamically.
77     */
78    @VisibleForTesting
79    public static void stop() {
80      stopped.set(true);
81      ScheduledFuture future = fut.get();
82      future.cancel(false);
83    }
84
85    /**
86     * Restarts the stopped service.
87     * @see #stop()
88     */
89    @VisibleForTesting
90    public static void restart() {
91      stopped.set(false);
92    }
93
94    final static class JmxCacheBusterRunnable implements Runnable {
95      @Override
96      public void run() {
97        if (LOG.isTraceEnabled()) {
98          LOG.trace("Clearing JMX mbean cache.");
99        }
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 }