View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.util;
20  
21  import org.apache.hadoop.hbase.classification.InterfaceAudience;
22  
23  /**
24   * Manages a singleton instance of the environment edge. This class shall
25   * implement static versions of the interface {@link EnvironmentEdge}, then
26   * defer to the delegate on invocation.
27   * <br>
28   * <b>Original Motivation:</b>
29   * The main purpose of the Environment Edge Manager was to have better control
30   * over the tests so that they behave the same when run in any system.
31   * (Refer: <a href="https://issues.apache.org/jira/browse/HBASE-2578">HBASE-2578</a> - The issue
32   * which added the {@link org.apache.hadoop.hbase.util.EnvironmentEdgeManager}).
33   * The idea is to have a central place where time can be assigned in HBase. That makes
34   * it easier to inject different implementations of time. The default environment edge is the Java
35   * Current Time in millis. The environment edge manager class is designed to be able
36   * to plug in a new implementation of time by simply injecting an implementation
37   * of {@link org.apache.hadoop.hbase.util.EnvironmentEdge} interface to
38   * {@link org.apache.hadoop.hbase.util.EnvironmentEdgeManager}
39  <p>
40   <b>Problems with Environment Edge:</b><br>
41   1. One of the major problems is the side effects of injecting an Environment Edge into
42      Environment Edge Manager.<br>
43      For example, A test could inject an edge to fast forward time in order to avoid thread
44      sleep to save time, but it could trigger a premature waking up of another thread waiting
45      on a condition dependent on time lapse, which could potentially affect the normal
46      working of the system leading to failure of tests.<br>
47   2. Every test should ensure it is setting the Environment Edge it needs for the test to
48      perform in an expected way. Because another test which might have run before the current test
49      could have injected its own custom Environment Edge which may not be applicable to this
50      test. This is still solvable but the problem is that the tests can run in parallel
51      leading to different combinations of environment edges being injected causing unexpected
52      results.<br>
53   3. Another important issue with respect to injecting time through Environment Edge is that
54      the milliseconds unit of time is ingrained throughout the codebase in the form of hardcoded
55      sleep time or timeouts that any change of time unit or making it fast or slow can potentially
56      trigger unexpected failures due to timeout or unintended flow of execution.<br>
57  </p>
58   Because of the above issues, only {@link org.apache.hadoop.hbase.util.DefaultEnvironmentEdge}
59   is being used, whose implementation of time returns the {@link System#currentTimeMillis()}. It
60   is advised not to inject any other {@link org.apache.hadoop.hbase.util.EnvironmentEdge}.
61   */
62  @InterfaceAudience.Private
63  public class EnvironmentEdgeManager {
64    private static volatile EnvironmentEdge delegate = new DefaultEnvironmentEdge();
65  
66    private EnvironmentEdgeManager() {
67  
68    }
69  
70    /**
71     * Retrieves the singleton instance of the {@link EnvironmentEdge} that is
72     * being managed.
73     *
74     * @return the edge.
75     */
76    public static EnvironmentEdge getDelegate() {
77      return delegate;
78    }
79  
80    /**
81     * Resets the managed instance to the default instance: {@link
82     * DefaultEnvironmentEdge}.
83     */
84    public static void reset() {
85      injectEdge(new DefaultEnvironmentEdge());
86    }
87  
88    /**
89     * Injects the given edge such that it becomes the managed entity. If null is
90     * passed to this method, the default type is assigned to the delegate.
91     *
92     * @param edge the new edge.
93     */
94    public static void injectEdge(EnvironmentEdge edge) {
95      if (edge == null) {
96        reset();
97      } else {
98        delegate = edge;
99      }
100   }
101 
102   /**
103    * Defers to the delegate and calls the
104    * {@link EnvironmentEdge#currentTime()} method.
105    *
106    * @return current time in millis according to the delegate.
107    */
108   public static long currentTime() {
109     return getDelegate().currentTime();
110   }
111 }