Skip to content

Commit

Permalink
completing javadocs
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimir-bukhtoyarov committed May 10, 2015
1 parent bb7cf7b commit 7ef06f4
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 33 deletions.
12 changes: 6 additions & 6 deletions src/main/java/com/github/bucket4j/AbstractBucket.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,21 @@ public void consume(long tokensToConsume) throws InterruptedException {
}

@Override
public boolean tryConsumeSingleToken(long maxWaitNanos) throws InterruptedException {
return tryConsume(1, maxWaitNanos);
public boolean tryConsumeSingleToken(long maxWaitTime) throws InterruptedException {
return tryConsume(1, maxWaitTime);
}

@Override
public boolean tryConsume(long tokensToConsume, long maxWaitNanos) throws InterruptedException {
public boolean tryConsume(long tokensToConsume, long maxWaitTime) throws InterruptedException {
if (tokensToConsume <= 0) {
throw nonPositiveTokensToConsume(tokensToConsume);
}

if (maxWaitNanos <= 0) {
throw nonPositiveNanosToWait(maxWaitNanos);
if (maxWaitTime <= 0) {
throw nonPositiveNanosToWait(maxWaitTime);
}

return consumeOrAwaitImpl(tokensToConsume, maxWaitNanos);
return consumeOrAwaitImpl(tokensToConsume, maxWaitTime);
}

@Override
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/github/bucket4j/BandwidthAdjuster.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,18 @@

import java.io.Serializable;

/**
* Provider of bandwidth capacity.
*/
public interface BandwidthAdjuster extends Serializable {

/**
* Return capacity of bandwidth which can depends from current time if needs.
*
* @param currentTime Cuurent time which returned by timeMeter {@link TimeMeter}
*
* @return
*/
long getCapacity(long currentTime);

public static class ImmutableCapacity implements BandwidthAdjuster {
Expand Down
70 changes: 49 additions & 21 deletions src/main/java/com/github/bucket4j/Bucket.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,15 @@
package com.github.bucket4j;

/**
* A token bucket implementation that is of a leaky bucket in the sense that it has a finite capacity and any added
* tokens that would exceed this capacity will "overflow" out of the bucket and are lost forever.
* <p/>
* In this implementation the rules for refilling the bucket are encapsulated in a provided {@code RefillStrategy}
* instance. Prior to attempting to consumeSingleToken any tokens the refill strategy will be consulted to see how many tokens
* should be added to the bucket.
* <p/>
* In addition in this implementation the method of yielding CPU control is encapsulated in the provided
* {@code SleepStrategy} instance. For high performance applications where tokens are being refilled incredibly quickly
* and an accurate bucket implementation is required, it may be useful to never yield control of the CPU and to instead
* busy wait. This strategy allows the caller to make this decision for themselves instead of the library forcing a
* decision.
* The continuous-state leaky bucket can be viewed as a finite capacity bucket
* whose real-valued content drains out at a continuous rate of 1 unit of content per time unit
* and whose content is increased by the increment T for each conforming cell...
* If at a cell arrival the content of the bucket is less than or equal to the limit value τ, then the cell is conforming;
* otherwise, the cell is non-conforming.
*
* @see <a href="http://en.wikipedia.org/wiki/Token_bucket">Token Bucket on Wikipedia</a>
* @see <a href="http://en.wikipedia.org/wiki/Leaky_bucket">Leaky Bucket on Wikipedia</a>
* @see <a href="http://en.wikipedia.org/wiki/Token_bucket">Token Bucket</a>
* @see <a href="http://en.wikipedia.org/wiki/Leaky_bucket">Leaky Bucket</a>
* @see <a href="http://en.wikipedia.org/wiki/Generic_cell_rate_algorithm">Generic cell rate algorithm</a>
*/
public interface Bucket {

Expand All @@ -46,36 +40,70 @@ public interface Bucket {
* Attempt to consume a specified number of tokens from the bucket. If the tokens were consumed then {@code true}
* is returned, otherwise {@code false} is returned.
*
* @param numTokens The number of tokens to consumeSingleToken from the bucket, must be a positive number.
* @param numTokens The number of tokens to consume from the bucket, must be a positive number.
* @return {@code true} if the tokens were consumed, {@code false} otherwise.
*/
boolean tryConsume(long numTokens);

/**
* Consumes as much tokens from bucket as available in the bucket in moment of invocation.
*
* @return number of tokens which has been consumed, or zero if was consumed nothing.
*/
long consumeAsMuchAsPossible();

/**
* Consumes as much tokens from bucket as available in the bucket in moment of invocation,
* but tokens which should be consumed is limited by than not more than {@code limit}.
*
* @return number of tokens which has been consumed, or zero if was consumed nothing.
*/
long consumeAsMuchAsPossible(long limit);

/**
* Consume a single token from the bucket. If no token is currently available then this method will block until a
* token becomes available.
* Consumes a single token from the bucket. If no token is currently available then this method will block until a
* token becomes available or current thread is interrupted. This is equivalent for {@code consume(1)}
*
* @throws InterruptedException
*/
void consumeSingleToken() throws InterruptedException;

/**
* Consumes multiple tokens from the bucket. If enough tokens are not currently available then this method will block
* until
* Consumes a single token from the bucket. If enough tokens are not currently available then this method will block
* until required number of tokens will be available or current thread is interrupted.
*
* @param numTokens The number of tokens to consumeSingleToken from teh bucket, must be a positive number.
*
* @throws InterruptedException
*/
void consume(long numTokens) throws InterruptedException;

boolean tryConsumeSingleToken(long maxWaitNanos) throws InterruptedException;
/**
* Consumes a single token from the bucket. If no token is currently available then this method will block
* until required number of tokens will be available or current thread is interrupted, or {@code maxWaitTime} has elapsed.
*
* This is equivalent for {@code tryConsume(1, maxWaitTime)}
*
* @param maxWaitTime limit of time which thread can wait.
*
* @return true if token has been consumed or false when token has not been consumed
*
* @throws InterruptedException
*/
boolean tryConsumeSingleToken(long maxWaitTime) throws InterruptedException;

boolean tryConsume(long numTokens, long maxWaitNanos) throws InterruptedException;
/**
* Consumes a specified number of tokens from the bucket. If required count of tokens is not currently available then this method will block
* until required number of tokens will be available or current thread is interrupted, or {@code maxWaitTime} has elapsed.
*
* @param numTokens The number of tokens to consume from the bucket.
* @param maxWaitTime limit of time which thread can wait.
*
* @return true if {@code numTokens} has been consumed or false when {@code numTokens} has not been consumed
*
* @throws InterruptedException
*/
boolean tryConsume(long numTokens, long maxWaitTime) throws InterruptedException;

BucketState createSnapshot();

Expand Down
93 changes: 87 additions & 6 deletions src/main/java/com/github/bucket4j/BucketBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public Bucket buildCustomGrid(GridProxy gridProxy) {
* {@code
* // Adds bandwidth which guarantees, that client of bucket will be able to consume 1 tokens per 10 minutes,
* // regardless of limitations.
* withGuaranteedBandwidth(1, TimeUnit.MINUTES, 10);
* builder.withGuaranteedBandwidth(1, TimeUnit.MINUTES, 10);
* }
* </pre>
*
Expand Down Expand Up @@ -169,7 +169,7 @@ public BucketBuilder withGuaranteedBandwidth(long maxCapacity, TimeUnit timeUnit
* {@code
* // Adds bandwidth which guarantees, that client of bucket will be able to consume 1 tokens per 10 minutes,
* // regardless of limitations. Size of bandwidth is 0 after construction
* withGuaranteedBandwidth(2, TimeUnit.MINUTES, 1, 0);
* builder.withGuaranteedBandwidth(2, TimeUnit.MINUTES, 1, 0);
* }
* </pre>
*
Expand All @@ -186,39 +186,116 @@ public BucketBuilder withGuaranteedBandwidth(long maxCapacity, TimeUnit timeUnit
return this;
}

/**
* Adds guaranteed bandwidth for all buckets which will be constructed by this builder instance.
* <p>
* Guaranteed bandwidth provides following feature: if tokens can be consumed from guaranteed bandwidth,
* then bucket4j do not perform checking of any limited bandwidths.
* <p>
* Unlike limited bandwidths, you can use only one guaranteed bandwidth per single bucket.
* <p>
*
* In opposite to method {@link #withGuaranteedBandwidth(long, TimeUnit, long)},
* this method does not perform checking of limitation
* which disallow to have greater rate of guaranteed than rate of limited bandwidth,
* because rate is dynamic and depends from <code>bandwidthAdjuster<code/>.
*
* @param bandwidthAdjuster provider of bandwidth capacity
* @param timeUnit Unit for period.
* @param period Period of bandwidth.
* @param initialCapacity initial capacity of bandwidth.
*
*/
public BucketBuilder withGuaranteedBandwidth(BandwidthAdjuster bandwidthAdjuster, TimeUnit timeUnit, long period, long initialCapacity) {
final long bandwidthPeriod = timeMeter.toBandwidthPeriod(timeUnit, period);
final BandwidthDefinition bandwidth = new BandwidthDefinition(bandwidthAdjuster, initialCapacity, bandwidthPeriod, true);
bandwidths.add(bandwidth);
return this;
}

/**
* Adds limited bandwidth for all buckets which will be constructed by this builder instance.
* <p>
* You can specify as many limited bandwidth as needed, but with following limitation: each limited bandwidth should has unique period,
* and when period of bandwidth <tt>X<tt/> is greater than bandwidth <tt>Y</tt>,
* then capacity of bandwidth <tt>X</tt> should be greater capacity of bandwidth <tt>Y</tt>,
* except cases when capacity of bandwidth <tt>X</tt> or <tt>Y</tt> is dynamic(provided by {@link com.github.bucket4j.BandwidthAdjuster}).
* <p>
* <pre>
* {@code
* // Adds bandwidth that restricts to consume not often 1 tokens per 10 minutes,
* builder.withLimitedBandwidth(1, TimeUnit.MINUTES, 10);
* }
* </pre>
*
* @param maxCapacity the maximum capacity of bandwidth
* @param timeUnit Unit for period.
* @param period Period of bandwidth.
*
*/
public BucketBuilder withLimitedBandwidth(long maxCapacity, TimeUnit timeUnit, long period) {
return withLimitedBandwidth(maxCapacity, timeUnit, period, maxCapacity);
}

/**
* Adds limited bandwidth for all buckets which will be constructed by this builder instance.
* <p>
* You can specify as many limited bandwidth as needed, but with following limitation: each limited bandwidth should has unique period,
* and when period of bandwidth <tt>X<tt/> is greater than bandwidth <tt>Y</tt>,
* then capacity of bandwidth <tt>X</tt> should be greater capacity of bandwidth <tt>Y</tt>,
* except cases when capacity of bandwidth <tt>X</tt> or <tt>Y</tt> is dynamic(provided by {@link com.github.bucket4j.BandwidthAdjuster}).
* <p>
* <pre>
* {@code
* // Adds bandwidth that restricts to consume not often 1 tokens per 10 minutes, and initial capacity 0.
* builder.withLimitedBandwidth(1, TimeUnit.MINUTES, 10, 0);
* }
* </pre>
*
* @param maxCapacity the maximum capacity of bandwidth
* @param timeUnit Unit for period.
* @param period Period of bandwidth.
* @param initialCapacity initial capacity
*
*/
public BucketBuilder withLimitedBandwidth(long maxCapacity, TimeUnit timeUnit, long period, long initialCapacity) {
final long bandwidthPeriod = timeMeter.toBandwidthPeriod(timeUnit, period);
final BandwidthDefinition bandwidth = new BandwidthDefinition(maxCapacity, initialCapacity, bandwidthPeriod, false);
bandwidths.add(bandwidth);
return this;
}

/**
* Adds limited bandwidth for all buckets which will be constructed by this builder instance.
* <p>
* You can specify as many limited bandwidth as needed, but with following limitation: each limited bandwidth should has unique period,
* and when period of bandwidth <tt>X<tt/> is greater than bandwidth <tt>Y</tt>,
* then capacity of bandwidth <tt>X</tt> should be greater capacity of bandwidth <tt>Y</tt>,
* except cases when capacity of bandwidth <tt>X</tt> or <tt>Y</tt> is dynamic(provided by {@link com.github.bucket4j.BandwidthAdjuster}).
*
* @param bandwidthAdjuster provider of bandwidth capacity
* @param timeUnit Unit for period.
* @param period Period of bandwidth.
* @param initialCapacity initial capacity
*
*/
public BucketBuilder withLimitedBandwidth(BandwidthAdjuster bandwidthAdjuster, TimeUnit timeUnit, long period, long initialCapacity) {
final long bandwidthPeriod = timeMeter.toBandwidthPeriod(timeUnit, period);
final BandwidthDefinition bandwidth = new BandwidthDefinition(bandwidthAdjuster, initialCapacity, bandwidthPeriod, false);
bandwidths.add(bandwidth);
return this;
}

public BandwidthDefinition getBandwidthDefinition(int index) {
return bandwidths.get(index);
}

/**
* @return Time meter used for time measuring.
*/
public TimeMeter getTimeMeter() {
return timeMeter;
}

/**
* @return configuration which used for bucket construction.
*/
public BucketConfiguration createConfiguration() {
return new BucketConfiguration(this.bandwidths, timeMeter);
}
Expand All @@ -231,4 +308,8 @@ public String toString() {
'}';
}

BandwidthDefinition getBandwidthDefinition(int index) {
return bandwidths.get(index);
}

}
20 changes: 20 additions & 0 deletions src/main/java/com/github/bucket4j/TimeMeter.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,31 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
* An abstraction over time measurement.
*
* @see com.github.bucket4j.TimeMeter#SYSTEM_NANOTIME
* @see com.github.bucket4j.TimeMeter#SYSTEM_MILLISECONDS
*/
public interface TimeMeter extends Serializable {

/**
* @return current time, which can be a milliseconds, nanoseconds, or something else in case of custom implementation.
*/
long currentTime();

/**
* Sleep required amount of time.
* @param units time to sleep
* @throws InterruptedException if current tread is interrupted.
*/
void sleep(long units) throws InterruptedException;

long toBandwidthPeriod(TimeUnit timeUnit, long period);

/**
* The implementation of {@link TimeMeter} which works arround {@link java.lang.System#nanoTime}
*/
public static final TimeMeter SYSTEM_NANOTIME = new TimeMeter() {
@Override
public long currentTime() {
Expand All @@ -54,6 +71,9 @@ public String toString() {
}
};

/**
* The implementation of {@link TimeMeter} which works around {@link java.lang.System#currentTimeMillis}
*/
public static final TimeMeter SYSTEM_MILLISECONDS = new TimeMeter() {
@Override
public long currentTime() {
Expand Down

0 comments on commit 7ef06f4

Please sign in to comment.