DefaultImmutableTokenBucket implements ImmutableTokenBucket { @Override public Optional<Pair<Long, ImmutableTokenBucket>> tryTake(long min, long max) { if (bucketLevel >= max) { return Optional.of(Pair.of(max, new DefaultImmutableTokenBucket(bucketSize, bucketLevel - max, refillStrategy))); } Pair<ImmutableRefillStrategy, Long> consumed = refillStrategy.consume(); long refill = consumed.getRight(); ImmutableRefillStrategy newRefillStrategyInstance = consumed.getLeft(); if (refill == 0) { if (bucketLevel >= min) { return Optional.of(Pair.of(bucketLevel, new DefaultImmutableTokenBucket(bucketSize, 0, refillStrategy))); } } else { long newBucketLevel = Math.min(bucketSize, bucketLevel + refill); if (newBucketLevel >= min) { long tokensToTake = Math.min(newBucketLevel, max); return Optional.of(Pair.of(tokensToTake, new DefaultImmutableTokenBucket(bucketSize, newBucketLevel - tokensToTake, newRefillStrategyInstance))); } } return Optional.empty(); } DefaultImmutableTokenBucket(long bucketSize, long bucketLevel, ImmutableRefillStrategy refillStrategy); @Override Optional<Pair<Long, ImmutableTokenBucket>> tryTake(long min, long max);  }
@Test public void testTryTake() throws Exception { ImmutableTokenBucket next = tokenBucket; for (int i = 0; i < BUCKET_SIZE; i++) { next = doTryTake(next); } assertThat(next.tryTake()).isEmpty(); testClock.advanceTime(REFILL_INTERVAL_MS, TimeUnit.MILLISECONDS); Pair<Long, ImmutableTokenBucket> pair = doTryTake(next, REFILL_BATCH, REFILL_BATCH); assertThat(pair.getLeft()).isEqualTo(REFILL_BATCH); assertThat(pair.getRight().tryTake()).isEmpty(); }
@Test public void testTryTakeRange() throws Exception { Pair<Long, ImmutableTokenBucket> next = doTryTake(tokenBucket, 1, BUCKET_SIZE + 1); assertThat(next.getLeft()).isEqualTo(BUCKET_SIZE); assertThat(next.getRight().tryTake()).isEmpty(); testClock.advanceTime(10 * REFILL_INTERVAL_MS, TimeUnit.MILLISECONDS); next = doTryTake(next.getRight(), 1, BUCKET_SIZE + 1); assertThat(next.getLeft()).isEqualTo(BUCKET_SIZE); assertThat(next.getRight().tryTake()).isEmpty(); }