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.hbase.util; 019 020import org.apache.yetus.audience.InterfaceAudience; 021 022/** 023 * Manages a singleton instance of the environment edge. This class shall implement static versions 024 * of the interface {@link EnvironmentEdge}, then defer to the delegate on invocation. <br> 025 * <b>Original Motivation:</b> The main purpose of the Environment Edge Manager was to have better 026 * control over the tests so that they behave the same when run in any system. (Refer: 027 * <a href="https://issues.apache.org/jira/browse/HBASE-2578">HBASE-2578</a> - The issue which added 028 * the {@link org.apache.hadoop.hbase.util.EnvironmentEdgeManager}). The idea is to have a central 029 * place where time can be assigned in HBase. That makes it easier to inject different 030 * implementations of time. The default environment edge is the Java Current Time in millis. The 031 * environment edge manager class is designed to be able to plug in a new implementation of time by 032 * simply injecting an implementation of {@link org.apache.hadoop.hbase.util.EnvironmentEdge} 033 * interface to {@link org.apache.hadoop.hbase.util.EnvironmentEdgeManager} 034 * <p> 035 * <b>Problems with Environment Edge:</b><br> 036 * 1. One of the major problems is the side effects of injecting an Environment Edge into 037 * Environment Edge Manager.<br> 038 * For example, A test could inject an edge to fast forward time in order to avoid thread sleep to 039 * save time, but it could trigger a premature waking up of another thread waiting on a condition 040 * dependent on time lapse, which could potentially affect the normal working of the system leading 041 * to failure of tests.<br> 042 * 2. Every test should ensure it is setting the Environment Edge it needs for the test to perform 043 * in an expected way. Because another test which might have run before the current test could have 044 * injected its own custom Environment Edge which may not be applicable to this test. This is still 045 * solvable but the problem is that the tests can run in parallel leading to different combinations 046 * of environment edges being injected causing unexpected results.<br> 047 * 3. Another important issue with respect to injecting time through Environment Edge is that the 048 * milliseconds unit of time is ingrained throughout the codebase in the form of hardcoded sleep 049 * time or timeouts that any change of time unit or making it fast or slow can potentially trigger 050 * unexpected failures due to timeout or unintended flow of execution.<br> 051 * </p> 052 * Because of the above issues, only {@link org.apache.hadoop.hbase.util.DefaultEnvironmentEdge} is 053 * being used, whose implementation of time returns the {@link System#currentTimeMillis()}. It is 054 * advised not to inject any other {@link org.apache.hadoop.hbase.util.EnvironmentEdge}. 055 */ 056@InterfaceAudience.Private 057public class EnvironmentEdgeManager { 058 private static volatile EnvironmentEdge delegate = new DefaultEnvironmentEdge(); 059 060 private EnvironmentEdgeManager() { 061 062 } 063 064 /** 065 * Retrieves the singleton instance of the {@link EnvironmentEdge} that is being managed. 066 * @return the edge. 067 */ 068 public static EnvironmentEdge getDelegate() { 069 return delegate; 070 } 071 072 /** 073 * Resets the managed instance to the default instance: {@link DefaultEnvironmentEdge}. 074 */ 075 public static void reset() { 076 injectEdge(new DefaultEnvironmentEdge()); 077 } 078 079 /** 080 * Injects the given edge such that it becomes the managed entity. If null is passed to this 081 * method, the default type is assigned to the delegate. 082 * @param edge the new edge. 083 */ 084 public static void injectEdge(EnvironmentEdge edge) { 085 if (edge == null) { 086 reset(); 087 } else { 088 delegate = edge; 089 } 090 } 091 092 /** 093 * Defers to the delegate and calls the {@link EnvironmentEdge#currentTime()} method. 094 * @return current time in millis according to the delegate. 095 */ 096 public static long currentTime() { 097 return getDelegate().currentTime(); 098 } 099}