/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.kernel.concurrent;
import com.liferay.portal.kernel.concurrent.BatchablePipe.IncreasableEntryWrapper;
import com.liferay.portal.kernel.test.ReflectionTestUtil;
import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
/**
* @author Shuyang Zhou
*/
public class BatchablePipeTest {
@ClassRule
public static final CodeCoverageAssertor codeCoverageAssertor =
CodeCoverageAssertor.INSTANCE;
@Test
public void testBatchPutAndGet() {
BatchablePipe<String, Integer> batchablePipe = new BatchablePipe<>();
// Batch same entry
IncreasableEntry<String, Integer> increasbleEntry1 =
new IntegerIncreasableEntry("1st", 1);
Assert.assertTrue(batchablePipe.put(increasbleEntry1));
Assert.assertFalse(batchablePipe.put(increasbleEntry1));
IncreasableEntry<String, Integer> batchedIncreasableEntry =
batchablePipe.take();
Assert.assertNotSame(increasbleEntry1, batchedIncreasableEntry);
Assert.assertEquals(
increasbleEntry1.getKey(), batchedIncreasableEntry.getKey());
Assert.assertEquals(2, (int)batchedIncreasableEntry.getValue());
Assert.assertNull(batchablePipe.take());
// Batch 2 entries
IncreasableEntry<String, Integer> increasbleEntry2 =
new IntegerIncreasableEntry("2nd", 1);
IncreasableEntry<String, Integer> increasbleEntry3 =
new IntegerIncreasableEntry("2nd", 2);
Assert.assertTrue(batchablePipe.put(increasbleEntry2));
Assert.assertFalse(batchablePipe.put(increasbleEntry3));
batchedIncreasableEntry = batchablePipe.take();
Assert.assertNotSame(increasbleEntry2, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry3, batchedIncreasableEntry);
Assert.assertEquals("2nd", batchedIncreasableEntry.getKey());
Assert.assertEquals(3, (int)batchedIncreasableEntry.getValue());
Assert.assertNull(batchablePipe.take());
// Mix batch
IncreasableEntry<String, Integer> increasbleEntry4 =
new IntegerIncreasableEntry("3rd", 1);
IncreasableEntry<String, Integer> increasbleEntry5 =
new IntegerIncreasableEntry("4th", 1);
IncreasableEntry<String, Integer> increasbleEntry6 =
new IntegerIncreasableEntry("4th", 2);
IncreasableEntry<String, Integer> increasbleEntry7 =
new IntegerIncreasableEntry("3rd", 3);
IncreasableEntry<String, Integer> increasbleEntry8 =
new IntegerIncreasableEntry("5th", 1);
Assert.assertTrue(batchablePipe.put(increasbleEntry4));
Assert.assertTrue(batchablePipe.put(increasbleEntry5));
Assert.assertFalse(batchablePipe.put(increasbleEntry6));
Assert.assertFalse(batchablePipe.put(increasbleEntry7));
Assert.assertTrue(batchablePipe.put(increasbleEntry8));
batchedIncreasableEntry = batchablePipe.take();
Assert.assertNotSame(increasbleEntry4, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry5, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry6, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry7, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry8, batchedIncreasableEntry);
Assert.assertEquals("4th", batchedIncreasableEntry.getKey());
Assert.assertEquals(3, (int)batchedIncreasableEntry.getValue());
batchedIncreasableEntry = batchablePipe.take();
Assert.assertNotSame(increasbleEntry4, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry5, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry6, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry7, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry8, batchedIncreasableEntry);
Assert.assertEquals("3rd", batchedIncreasableEntry.getKey());
Assert.assertEquals(4, (int)batchedIncreasableEntry.getValue());
batchedIncreasableEntry = batchablePipe.take();
Assert.assertNotSame(increasbleEntry4, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry5, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry6, batchedIncreasableEntry);
Assert.assertNotSame(increasbleEntry7, batchedIncreasableEntry);
Assert.assertSame(increasbleEntry8, batchedIncreasableEntry);
Assert.assertNull(batchablePipe.take());
}
@Test
public void testConcurrent() throws InterruptedException {
final BatchablePipe<String, Integer> batchablePipe =
new BatchablePipe<>();
final BlockingQueue<IncreasableEntry<String, Integer>>
resultBlockingQueue = new LinkedBlockingQueue<>();
ExecutorService putThreadPool = Executors.newFixedThreadPool(5);
ExecutorService takeThreadPool = Executors.newFixedThreadPool(5);
Runnable putRunnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
batchablePipe.put(
new IntegerIncreasableEntry(String.valueOf(i % 10), 1));
}
}
};
Runnable takeRunnable = new Runnable() {
@Override
public void run() {
while (true) {
try {
IncreasableEntry<String, Integer> increasableEntry =
batchablePipe.take();
if (increasableEntry != null) {
String key = increasableEntry.getKey();
if (key.equals("exit")) {
int value = increasableEntry.getValue();
if (value > 1) {
Assert.assertTrue(
batchablePipe.put(
new IntegerIncreasableEntry(
"exit", value - 1)));
}
return;
}
resultBlockingQueue.put(increasableEntry);
}
}
catch (InterruptedException ie) {
}
}
}
};
// Submit jobs
for (int i = 0; i < 10; i++) {
putThreadPool.submit(putRunnable);
takeThreadPool.submit(takeRunnable);
}
// Wait until put finish
putThreadPool.shutdown();
putThreadPool.awaitTermination(240, TimeUnit.SECONDS);
// Poison take thread pool
IncreasableEntry<String, Integer> poisonIncreasableEntry =
new IntegerIncreasableEntry("exit", 10);
Assert.assertTrue(batchablePipe.put(poisonIncreasableEntry));
takeThreadPool.shutdown();
takeThreadPool.awaitTermination(240, TimeUnit.SECONDS);
// Do statistics
Map<String, Integer> verifyMap = new HashMap<>();
for (IncreasableEntry<String, Integer> increasableEntry :
resultBlockingQueue) {
String key = increasableEntry.getKey();
Integer value = increasableEntry.getValue();
Integer sum = verifyMap.get(key);
if (sum == null) {
verifyMap.put(key, value);
}
else {
verifyMap.put(key, sum + value);
}
}
// Verify statistics
for (int i = 0; i < 10; i++) {
Integer sum = verifyMap.get(String.valueOf(i));
Assert.assertEquals(100, (int)sum);
}
}
@Test
public void testConcurrentPut() {
BatchablePipe<String, Integer> batchablePipe = new BatchablePipe<>();
final IncreasableEntry<String, Integer> increasbleEntry1 =
new IntegerIncreasableEntry("test", 1);
final IncreasableEntry<String, Integer> increasbleEntry2 =
new IntegerIncreasableEntry("test", 2);
IncreasableEntry<String, Integer> increasbleEntry3 =
new IntegerIncreasableEntry("test", 3);
ReflectionTestUtil.setFieldValue(
batchablePipe, "concurrentMap",
new ConcurrentHashMap
<String, IncreasableEntryWrapper<String, Integer>>() {
@Override
public boolean replace(
String key,
IncreasableEntryWrapper<String, Integer> oldValue,
IncreasableEntryWrapper<String, Integer> newValue) {
if (oldValue.increasableEntry == increasbleEntry1) {
put(
key,
new IncreasableEntryWrapper<String, Integer>(
increasbleEntry2));
}
return super.replace(key, oldValue, newValue);
}
});
Assert.assertTrue(batchablePipe.put(increasbleEntry1));
Assert.assertFalse(batchablePipe.put(increasbleEntry3));
IncreasableEntry<String, Integer> batchedIncreasableEntry =
batchablePipe.take();
Assert.assertEquals(5, (int)batchedIncreasableEntry.getValue());
Assert.assertNull(batchablePipe.take());
}
@Test
public void testCreation() {
BatchablePipe<String, Integer> batchablePipe = new BatchablePipe<>();
Assert.assertNull(batchablePipe.take());
Assert.assertNull(batchablePipe.take());
Assert.assertNull(batchablePipe.take());
}
@Test
public void testIncreasableEntryWrapper() {
IncreasableEntry<String, Integer> increasbleEntry1 =
new IntegerIncreasableEntry("test", 1);
IncreasableEntry<String, Integer> increasbleEntry2 =
new IntegerIncreasableEntry("test", 1);
Assert.assertEquals(
new IncreasableEntryWrapper<String, Integer>(increasbleEntry1),
new IncreasableEntryWrapper<String, Integer>(increasbleEntry1));
Assert.assertNotEquals(
new IncreasableEntryWrapper<String, Integer>(increasbleEntry1),
new IncreasableEntryWrapper<String, Integer>(increasbleEntry2));
IncreasableEntryWrapper<String, Integer> increasableEntryWrapper =
new IncreasableEntryWrapper<>(increasbleEntry1);
Assert.assertEquals(
increasbleEntry1.hashCode(), increasableEntryWrapper.hashCode());
Assert.assertEquals(
increasbleEntry1.toString(), increasableEntryWrapper.toString());
}
@Test
public void testSimplePutAndTake() {
BatchablePipe<String, Integer> batchablePipe = new BatchablePipe<>();
// Put 1st
IncreasableEntry<String, Integer> increasableEntry1 =
new IntegerIncreasableEntry("1st", 1);
Assert.assertTrue(batchablePipe.put(increasableEntry1));
// Get 1st
Assert.assertSame(increasableEntry1, batchablePipe.take());
// Sequence put
IncreasableEntry<String, Integer> increasableEntry2 =
new IntegerIncreasableEntry("2nd", 2);
Assert.assertTrue(batchablePipe.put(increasableEntry2));
IncreasableEntry<String, Integer> increasableEntry3 =
new IntegerIncreasableEntry("3nd", 3);
Assert.assertTrue(batchablePipe.put(increasableEntry3));
IncreasableEntry<String, Integer> increasableEntry4 =
new IntegerIncreasableEntry("4th", 4);
Assert.assertTrue(batchablePipe.put(increasableEntry4));
// Sequence take
// Get 2nd
Assert.assertSame(increasableEntry2, batchablePipe.take());
// Get 3rd
Assert.assertSame(increasableEntry3, batchablePipe.take());
// Get 4th
Assert.assertSame(increasableEntry4, batchablePipe.take());
Assert.assertNull(batchablePipe.take());
}
private static class IntegerIncreasableEntry
extends IncreasableEntry<String, Integer> {
public IntegerIncreasableEntry(String key, Integer value) {
super(key, value);
}
@Override
public IntegerIncreasableEntry increase(Integer deltaValue) {
return new IntegerIncreasableEntry(key, value + deltaValue);
}
}
}