/*
* Copyright Terracotta, Inc.
*
* Licensed 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 org.ehcache.clustered.client.config;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
/**
* Describes a timeout value for a clustered operation. Instances of this class
* are subject to the operation of the Java {@code java.util.concurrent.TimeUnit} class.
*/
public final class TimeoutDuration {
private final long amount;
private final TimeUnit unit;
private TimeoutDuration(long amount, TimeUnit unit) {
this.amount = amount;
this.unit = unit;
}
/**
* Constant indicating no timeout.
*/
public static final TimeoutDuration NONE = new TimeoutDuration(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
/**
* Gets a {@code TimeoutDuration} of the indicated duration.
*
* @param amount the non-negative timeout duration
* @param unit the non-{@code null} units for {@code amount}
*
* @return a {@code TimeoutDuration} instance for the specified duration
*
* @throws NullPointerException if {@code unit} is {@code null}
* @throws IllegalArgumentException if {@code amount} is negative
*/
public static TimeoutDuration of(long amount, TimeUnit unit) {
if (unit == null) {
throw new NullPointerException("TimeoutDuration unit can not be null");
}
if (amount < 0) {
throw new IllegalArgumentException("TimeoutDuration time amount must be non-negative");
}
if (amount == NONE.amount && unit == NONE.unit) {
return NONE;
}
return new TimeoutDuration(amount, unit);
}
/**
* Converts this {@code TimeoutDuration} to nanoseconds. Values are converted
* according to the rules for {@code java.util.concurrent.TimeUnit}.
*
* @return the number of nanoseconds represented by this {@code TimeDuration}
*/
public long toNanos() {
return unit.toNanos(amount);
}
/**
* Converts this {@code TimeoutDuration} to milliseconds. Values are converted
* according to the rules for {@code java.util.concurrent.TimeUnit}.
*
* @return the number of milliseconds represented by this {@code TimeDuration}
*/
public long toMillis() {
return unit.toMillis(amount);
}
/**
* Converts this {@code TimeDuration} to the specified time unit. Values are
* converted according to the rules for {@code java.util.concurrent.TimeUnit}.
*
* @param toUnit the {@code TimeUnit} to which this {@code TimeDuration} value is converted
*
* @return the duration expressed in {@code toUnit} units
*
* @see TimeUnit#convert(long, TimeUnit)
*/
public long convert(TimeUnit toUnit) {
return toUnit.convert(amount, unit);
}
/**
* Performs a timed wait on the object provided.
*
* @param obj the {@code Object} on which to wait
*
* @throws InterruptedException if the wait is interrupted
*
* @see TimeUnit#timedWait(Object, long)
*/
public void timedWait(Object obj) throws InterruptedException {
unit.timedWait(obj, amount);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || this.getClass() != other.getClass()) {
return false;
}
TimeoutDuration that = (TimeoutDuration)other;
if (this.amount == 0 && that.amount == 0) {
return true; // A duration of 0 is the same in any units
} else if (this.unit == that.unit) {
return this.amount == that.amount;
} else {
/*
* Select the more precise/granular unit for the basis of the equals comparison. Since
* TimeUnit.convert _truncates_ fractional values, attempting to convert one a more
* precise/granular unit to a less precise/granular unit will result in zero -- indicating
* which is the more precise/granular unit.
*/
TimeoutDuration lessGranular;
TimeoutDuration moreGranular;
if (this.unit.convert(1, that.unit) > 0) {
// No truncation -- 'this' has the more granular unit
lessGranular = that;
moreGranular = this;
} else {
// Truncated -- 'that' has the more granular unit
lessGranular = this;
moreGranular = that;
}
return BigInteger.valueOf(moreGranular.unit.convert(1, lessGranular.unit))
.multiply(BigInteger.valueOf(lessGranular.amount))
.equals(BigInteger.valueOf(moreGranular.amount));
}
}
@Override
public int hashCode() {
return BigInteger.valueOf(TimeUnit.NANOSECONDS.convert(1, this.unit)).multiply(BigInteger.valueOf(this.amount)).hashCode();
}
@Override
public String toString() {
return (this == NONE ? "TimeoutDuration.NONE" : "TimeoutDuration{" + amount + ' ' + unit + '}');
}
}