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}