001/*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.util;
020
021import org.apache.yetus.audience.InterfaceAudience;
022
023/**
024 * Manages a singleton instance of the environment edge. This class shall
025 * implement static versions of the interface {@link EnvironmentEdge}, then
026 * defer to the delegate on invocation.
027 * <br>
028 * <b>Original Motivation:</b>
029 * The main purpose of the Environment Edge Manager was to have better control
030 * over the tests so that they behave the same when run in any system.
031 * (Refer: <a href="https://issues.apache.org/jira/browse/HBASE-2578">HBASE-2578</a> - The issue
032 * which added the {@link org.apache.hadoop.hbase.util.EnvironmentEdgeManager}).
033 * The idea is to have a central place where time can be assigned in HBase. That makes
034 * it easier to inject different implementations of time. The default environment edge is the Java
035 * Current Time in millis. The environment edge manager class is designed to be able
036 * to plug in a new implementation of time by simply injecting an implementation
037 * of {@link org.apache.hadoop.hbase.util.EnvironmentEdge} interface to
038 * {@link org.apache.hadoop.hbase.util.EnvironmentEdgeManager}
039<p>
040 <b>Problems with Environment Edge:</b><br>
041 1. One of the major problems is the side effects of injecting an Environment Edge into
042    Environment Edge Manager.<br>
043    For example, A test could inject an edge to fast forward time in order to avoid thread
044    sleep to save time, but it could trigger a premature waking up of another thread waiting
045    on a condition dependent on time lapse, which could potentially affect the normal
046    working of the system leading to failure of tests.<br>
047 2. Every test should ensure it is setting the Environment Edge it needs for the test to
048    perform in an expected way. Because another test which might have run before the current test
049    could have injected its own custom Environment Edge which may not be applicable to this
050    test. This is still solvable but the problem is that the tests can run in parallel
051    leading to different combinations of environment edges being injected causing unexpected
052    results.<br>
053 3. Another important issue with respect to injecting time through Environment Edge is that
054    the milliseconds unit of time is ingrained throughout the codebase in the form of hardcoded
055    sleep time or timeouts that any change of time unit or making it fast or slow can potentially
056    trigger unexpected failures due to timeout or unintended flow of execution.<br>
057</p>
058 Because of the above issues, only {@link org.apache.hadoop.hbase.util.DefaultEnvironmentEdge}
059 is being used, whose implementation of time returns the {@link System#currentTimeMillis()}. It
060 is advised not to inject any other {@link org.apache.hadoop.hbase.util.EnvironmentEdge}.
061 */
062@InterfaceAudience.Private
063public class EnvironmentEdgeManager {
064  private static volatile EnvironmentEdge delegate = new DefaultEnvironmentEdge();
065
066  private EnvironmentEdgeManager() {
067
068  }
069
070  /**
071   * Retrieves the singleton instance of the {@link EnvironmentEdge} that is
072   * being managed.
073   *
074   * @return the edge.
075   */
076  public static EnvironmentEdge getDelegate() {
077    return delegate;
078  }
079
080  /**
081   * Resets the managed instance to the default instance: {@link
082   * DefaultEnvironmentEdge}.
083   */
084  public static void reset() {
085    injectEdge(new DefaultEnvironmentEdge());
086  }
087
088  /**
089   * Injects the given edge such that it becomes the managed entity. If null is
090   * passed to this method, the default type is assigned to the delegate.
091   *
092   * @param edge the new edge.
093   */
094  public static void injectEdge(EnvironmentEdge edge) {
095    if (edge == null) {
096      reset();
097    } else {
098      delegate = edge;
099    }
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}