package org.simpleframework.common.lease; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.simpleframework.common.lease.Cleaner; import org.simpleframework.common.lease.Lease; import org.simpleframework.common.lease.LeaseManager; public class LeaseManagerTest extends TimeTestCase { private static int ITERATIONS = 1000; private static int MAXIMUM = 20000; static { String value = System.getProperty("iterations"); if (value != null) { ITERATIONS = Integer.parseInt(value); } } public void testClock() { List<Long> timeList = new ArrayList<Long>(); for(int i = 0; i < ITERATIONS; i++) { long time = System.nanoTime(); long milliseconds = TimeUnit.MILLISECONDS.convert(time, TimeUnit.MILLISECONDS); timeList.add(milliseconds); } for(int i = 1; i < ITERATIONS; i++) { assertLessThanOrEqual(timeList.get(i - 1), timeList.get(i)); } } public void testRandom() { for(int i = 0; i < ITERATIONS; i++) { long randomTime = getRandomTime(MAXIMUM); assertGreaterThanOrEqual(MAXIMUM, randomTime); assertGreaterThanOrEqual(randomTime, 0); } } public void testOrder() throws Exception { final BlockingQueue<Integer> clean = new LinkedBlockingQueue<Integer>(); final ConcurrentHashMap<Integer, Long> record = new ConcurrentHashMap<Integer, Long>(); Cleaner<Integer> cleaner = new Cleaner<Integer>() { long start = System.currentTimeMillis(); public void clean(Integer key) { record.put(key, start - System.currentTimeMillis()); clean.offer(key); } }; LeaseManager<Integer> manager = new LeaseManager<Integer>(cleaner); List<Lease<Integer>> list = new ArrayList<Lease<Integer>>(); long start = System.currentTimeMillis(); for(int i = 0; i < ITERATIONS; i++) { long randomTime = getRandomTime(MAXIMUM) + MAXIMUM + i * 50; System.err.printf("leasing [%s] for [%s] @ %s%n", i, randomTime, System.currentTimeMillis() - start); Lease<Integer> lease = manager.lease(i, randomTime, TimeUnit.MILLISECONDS); list.add(lease); } start = System.currentTimeMillis(); for(int i = 0; i < ITERATIONS; i++) { try { System.err.printf("renewing [%s] for [%s] expires [%s] @ %s expired [%s] %n", i, i, list.get(i).getExpiry(TimeUnit.MILLISECONDS), System.currentTimeMillis() - start, record.get(i)); list.get(i).renew(i, TimeUnit.MILLISECONDS); }catch(Exception e) { System.err.printf("Lease %s in error: ", i); e.printStackTrace(System.err); } } int variation = 20; int cleaned = 0; for(int i = 0; i < ITERATIONS; i++) { int value = clean.take(); cleaned++; System.err.printf("index=[%s] clean=[%s] expiry[%s]=%s expiry[%s]=%s%n ", i, value, i, record.get(i), value, record.get(value)); assertLessThanOrEqual(i - variation, value); } assertEquals(cleaned, ITERATIONS); } public void testLease() throws Exception { final BlockingQueue<Expectation> clean = new LinkedBlockingQueue<Expectation>(); Cleaner<Expectation> cleaner = new Cleaner<Expectation>() { public void clean(Expectation key) { clean.offer(key); } }; final BlockingQueue<Lease<Expectation>> renewalQueue = new LinkedBlockingQueue<Lease<Expectation>>(); final BlockingQueue<Lease<Expectation>> expiryQueue = new LinkedBlockingQueue<Lease<Expectation>>(); final CountDownLatch ready = new CountDownLatch(21); final AtomicInteger renewCount = new AtomicInteger(ITERATIONS); for(int i = 0; i < 20; i++) { new Thread(new Runnable() { public void run() { while(renewCount.getAndDecrement() > 0) { long randomTime = getRandomTime(MAXIMUM); try { ready.countDown(); ready.await(); Lease<Expectation> lease = renewalQueue.take(); try { lease.renew(randomTime, TimeUnit.MILLISECONDS); lease.getKey().setExpectation(randomTime, TimeUnit.MILLISECONDS); assertGreaterThanOrEqual(randomTime, 0); assertGreaterThanOrEqual(randomTime, lease.getExpiry(TimeUnit.MILLISECONDS)); } catch(Exception e) { expiryQueue.offer(lease); } } catch(Exception e) { e.printStackTrace(); } } } }).start(); } final LeaseManager<Expectation> manager = new LeaseManager<Expectation>(cleaner); final CountDownLatch latch = new CountDownLatch(21); final AtomicInteger leaseCount = new AtomicInteger(ITERATIONS); for(int i = 0; i < 20; i++) { new Thread(new Runnable() { public void run() { while(leaseCount.getAndDecrement() > 0) { long randomTime = getRandomTime(MAXIMUM); Expectation expectation = new Expectation(randomTime, TimeUnit.MILLISECONDS); try { latch.countDown(); latch.await(); } catch(InterruptedException e) { e.printStackTrace(); } assertGreaterThanOrEqual(randomTime, 0); Lease<Expectation> lease = manager.lease(expectation, randomTime, TimeUnit.MILLISECONDS); renewalQueue.offer(lease); } } }).start(); } ready.countDown(); latch.countDown(); for (int i = 0; i < ITERATIONS; i++) { Expectation expectation = clean.poll(MAXIMUM, TimeUnit.MILLISECONDS); if(expectation != null) { long accuracy = System.nanoTime() - expectation.getExpectation(TimeUnit.NANOSECONDS); long milliseconds = TimeUnit.MILLISECONDS.convert(accuracy, TimeUnit.NANOSECONDS); System.err.printf("index=[%s] accuracy=[%s] queue=[%s]%n", i, milliseconds, clean.size()); } else { System.err.printf("index=[%s] queue=[%s]%n", i, clean.size()); } } System.err.printf("waiting=[%s]%n", clean.size()); } public static class Expectation { private long time; public Expectation(long duration, TimeUnit unit) { setExpectation(duration, unit); } public void setExpectation(long duration, TimeUnit unit) { long nano = TimeUnit.NANOSECONDS.convert(duration, unit); long expect = nano + System.nanoTime(); this.time = expect; } public long getExpectation(TimeUnit unit) { return unit.convert(time, TimeUnit.NANOSECONDS); } } public static long getRandomTime(long maximum) { long random = new Random().nextLong() % maximum; if(random < 0) { random *= -1; } return random; } public static void main(String[] list) throws Exception { new LeaseManagerTest().testClock(); new LeaseManagerTest().testRandom(); new LeaseManagerTest().testOrder(); new LeaseManagerTest().testLease(); } }