/** * Licensed to Cloudera, Inc. under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Cloudera, Inc. licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.cloudera.flume.util; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.cloudera.util.Clock; /** * This is a wrapper and replacement for normal system calls related to time. So * far it support system clock and calls to Thread.sleep. * * It uses wait() and notify() for simulating a sleep in a separate thread. */ public class MockClock extends Clock { static final Logger LOG = LoggerFactory.getLogger(MockClock.class); long time = 0; Object lock = new Object(); /** * Construct with specified start time. (in millis or unix time) */ public MockClock(long start) { time = start; } /** * Get the "current" date */ @Override public Date getDate() { return new Date(time); } /** * Get the current nanos. This is mostly used as a tie breaker if unixtime * doesn't have enough resolution -- subsequent class will be "slightly" after * earlier ones. */ @Override public long getNanos() { return System.nanoTime(); } /** * Get the "current" unix time. Note that multiple calls to this method * without a forward call will return the same time! Use getNanos as a tie * breaker. */ @Override public long getUnixTime() { return time; } /** * Manually move time forward. */ public void forward(long millis) { synchronized (lock) { time += millis; lock.notifyAll(); // alert any waiting sleeps LOG.debug("running thread: " + this); } try { Thread.sleep(0); // yield to other threads. } catch (InterruptedException e) { // This should never happen } } public String toString() { return "virtual time: " + time; } /** * We simulate a sleep by checking the "time" and calling lock.wait() if the * "until" time hasn't been reached yet. Each call to forward sends a notify * which will wake the wait and check to see if it can fall through again. If * it cannot fall through we go back to wait. */ @Override public void doSleep(long millis) throws InterruptedException { synchronized (lock) { long until = time + millis; while (this.getUnixTime() < until) { lock.wait(); // wait until condition is met. } } LOG.debug("sleeping thread awoke: " + this); } }