/**
* 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.memory.FinalizeManager;
import com.liferay.portal.kernel.test.CaptureHandler;
import com.liferay.portal.kernel.test.GCUtil;
import com.liferay.portal.kernel.test.JDKLoggerTestUtil;
import com.liferay.portal.kernel.test.ReflectionTestUtil;
import com.liferay.portal.kernel.test.rule.AggregateTestRule;
import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
import com.liferay.portal.kernel.test.rule.NewEnv;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.test.aspects.ReflectionUtilAdvice;
import com.liferay.portal.test.rule.AdviseWith;
import com.liferay.portal.test.rule.AspectJNewEnvTestRule;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
/**
* @author Shuyang Zhou
*/
public class AsyncBrokerTest {
@ClassRule
@Rule
public static final AggregateTestRule aggregateTestRule =
new AggregateTestRule(
CodeCoverageAssertor.INSTANCE, AspectJNewEnvTestRule.INSTANCE);
@After
public void tearDown() {
System.clearProperty(_THREAD_ENABLED_KEY);
}
@Test
public void testGetOpenBids() {
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
Map<String, NoticeableFuture<String>> map = asyncBroker.getOpenBids();
Assert.assertTrue(map.isEmpty());
try {
map.clear();
Assert.fail();
}
catch (UnsupportedOperationException uoe) {
}
NoticeableFuture<String> noticeableFuture = asyncBroker.post(_KEY);
Assert.assertEquals(map.toString(), 1, map.size());
Assert.assertSame(noticeableFuture, map.get(_KEY));
noticeableFuture.cancel(true);
Assert.assertTrue(map.isEmpty());
}
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@Test
public void testOrphanCancellationAlreadyDone()
throws InterruptedException {
System.setProperty(_THREAD_ENABLED_KEY, StringPool.FALSE);
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
NoticeableFuture<String> noticeableFuture = asyncBroker.post(_KEY);
noticeableFuture.cancel(true);
noticeableFuture = null;
GCUtil.gc(true);
ReflectionTestUtil.invoke(
FinalizeManager.class, "_pollingCleanup", new Class<?>[0]);
}
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@Test
public void testOrphanCancellationNotDoneYet() throws InterruptedException {
// Without log
System.setProperty(_THREAD_ENABLED_KEY, StringPool.FALSE);
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
AsyncBroker.class.getName(), Level.OFF)) {
asyncBroker.post(_KEY);
GCUtil.gc(true);
ReflectionTestUtil.invoke(
FinalizeManager.class, "_pollingCleanup", new Class<?>[0]);
List<LogRecord> logRecords = captureHandler.getLogRecords();
Assert.assertTrue(logRecords.isEmpty());
}
// With log
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
AsyncBroker.class.getName(), Level.WARNING)) {
NoticeableFuture<String> noticeableFuture = asyncBroker.post(_KEY);
String toString = noticeableFuture.toString();
noticeableFuture = null;
GCUtil.gc(true);
ReflectionTestUtil.invoke(
FinalizeManager.class, "_pollingCleanup", new Class<?>[0]);
List<LogRecord> logRecords = captureHandler.getLogRecords();
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
LogRecord logRecord = logRecords.get(0);
Assert.assertEquals(
"Cancelled orphan noticeable future " + toString +
" with key " + _KEY,
logRecord.getMessage());
}
}
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@Test
public void testOrphanCancellationNotSupported() throws Exception {
System.setProperty(_THREAD_ENABLED_KEY, StringPool.FALSE);
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
AsyncBroker.class.getName(), Level.SEVERE)) {
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
asyncBroker.post(_KEY);
GCUtil.gc(true);
Field field = ReflectionTestUtil.getFieldValue(
AsyncBroker.class, "_REFERENT_FIELD");
field.setAccessible(false);
ReflectionTestUtil.invoke(
FinalizeManager.class, "_pollingCleanup", new Class<?>[0]);
List<LogRecord> logRecords = captureHandler.getLogRecords();
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
LogRecord logRecord = logRecords.get(0);
String message = logRecord.getMessage();
Assert.assertTrue(
message.startsWith("Unable to access referent of "));
Throwable throwable = logRecord.getThrown();
Assert.assertSame(
IllegalAccessException.class, throwable.getClass());
}
}
@AdviseWith(adviceClasses = ReflectionUtilAdvice.class)
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@Test
public void testPhantomReferenceResurrectionNotSupportedWithLog()
throws ClassNotFoundException {
testPhantomReferenceResurrectionNotSupported(true);
}
@AdviseWith(adviceClasses = ReflectionUtilAdvice.class)
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@Test
public void testPhantomReferenceResurrectionNotSupportedWithoutLog()
throws ClassNotFoundException {
testPhantomReferenceResurrectionNotSupported(false);
}
@Test
public void testPost() throws Exception {
ReflectionUtilAdvice.setDeclaredFieldThrowable(new Throwable());
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
ReflectionUtilAdvice.setDeclaredFieldThrowable(null);
Map<String, DefaultNoticeableFuture<String>> defaultNoticeableFutures =
ReflectionTestUtil.getFieldValue(
asyncBroker, "_defaultNoticeableFutures");
NoticeableFuture<String> noticeableFuture = asyncBroker.post(_KEY);
Assert.assertEquals(
defaultNoticeableFutures.toString(), 1,
defaultNoticeableFutures.size());
Assert.assertSame(noticeableFuture, defaultNoticeableFutures.get(_KEY));
Assert.assertSame(noticeableFuture, asyncBroker.post(_KEY));
Assert.assertEquals(
defaultNoticeableFutures.toString(), 1,
defaultNoticeableFutures.size());
Assert.assertTrue(noticeableFuture.cancel(true));
Assert.assertTrue(defaultNoticeableFutures.isEmpty());
}
@AdviseWith(adviceClasses = ReflectionUtilAdvice.class)
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@Test
public void testPostPhantomReferenceResurrectionNotSupported()
throws Exception {
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
AsyncBroker.class.getName(), Level.WARNING)) {
testPost();
List<LogRecord> logRecords = captureHandler.getLogRecords();
Assert.assertEquals(logRecords.toString(), 1, logRecords.size());
LogRecord logRecord = logRecords.get(0);
Assert.assertEquals(
"Cancellation of orphaned noticeable futures is disabled " +
"because the JVM does not support phantom reference " +
"resurrection",
logRecord.getMessage());
Throwable throwable = logRecord.getThrown();
Assert.assertSame(Throwable.class, throwable.getClass());
}
}
@Test
public void testTake() {
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
Map<String, DefaultNoticeableFuture<String>> defaultNoticeableFutures =
ReflectionTestUtil.getFieldValue(
asyncBroker, "_defaultNoticeableFutures");
Assert.assertTrue(defaultNoticeableFutures.isEmpty());
Assert.assertNull(asyncBroker.take(_KEY));
NoticeableFuture<String> noticeableFuture = asyncBroker.post(_KEY);
Assert.assertEquals(
defaultNoticeableFutures.toString(), 1,
defaultNoticeableFutures.size());
Assert.assertSame(noticeableFuture, defaultNoticeableFutures.get(_KEY));
Assert.assertSame(noticeableFuture, asyncBroker.take(_KEY));
Assert.assertTrue(defaultNoticeableFutures.isEmpty());
Assert.assertNull(asyncBroker.take(_KEY));
Assert.assertTrue(noticeableFuture.cancel(true));
}
@Test
public void testTakeWithException() throws Exception {
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
Map<String, DefaultNoticeableFuture<String>> defaultNoticeableFutures =
ReflectionTestUtil.getFieldValue(
asyncBroker, "_defaultNoticeableFutures");
Assert.assertTrue(defaultNoticeableFutures.isEmpty());
Exception exception = new Exception();
Assert.assertFalse(asyncBroker.takeWithException(_KEY, exception));
NoticeableFuture<String> noticeableFuture = asyncBroker.post(_KEY);
Assert.assertEquals(
defaultNoticeableFutures.toString(), 1,
defaultNoticeableFutures.size());
Assert.assertSame(noticeableFuture, defaultNoticeableFutures.get(_KEY));
Assert.assertTrue(asyncBroker.takeWithException(_KEY, exception));
try {
noticeableFuture.get();
Assert.fail();
}
catch (ExecutionException ee) {
Assert.assertSame(exception, ee.getCause());
}
Assert.assertTrue(defaultNoticeableFutures.isEmpty());
Assert.assertFalse(asyncBroker.takeWithException(_KEY, exception));
}
@Test
public void testTakeWithResult() throws Exception {
AsyncBroker<String, String> asyncBroker = new AsyncBroker<>();
Map<String, DefaultNoticeableFuture<String>> defaultNoticeableFutures =
ReflectionTestUtil.getFieldValue(
asyncBroker, "_defaultNoticeableFutures");
Assert.assertTrue(defaultNoticeableFutures.isEmpty());
Assert.assertFalse(asyncBroker.takeWithResult(_KEY, _VALUE));
NoticeableFuture<String> noticeableFuture = asyncBroker.post(_KEY);
Assert.assertEquals(
defaultNoticeableFutures.toString(), 1,
defaultNoticeableFutures.size());
Assert.assertSame(noticeableFuture, defaultNoticeableFutures.get(_KEY));
Assert.assertTrue(asyncBroker.takeWithResult(_KEY, _VALUE));
Assert.assertEquals(_VALUE, noticeableFuture.get());
Assert.assertTrue(defaultNoticeableFutures.isEmpty());
Assert.assertFalse(asyncBroker.takeWithResult(_KEY, _VALUE));
}
protected void testPhantomReferenceResurrectionNotSupported(boolean withLog)
throws ClassNotFoundException {
Throwable throwable = new Throwable();
ReflectionUtilAdvice.setDeclaredFieldThrowable(throwable);
Level level = Level.OFF;
if (withLog) {
level = Level.WARNING;
}
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
AsyncBroker.class.getName(), level)) {
Class.forName(
AsyncBroker.class.getName(), true,
AsyncBroker.class.getClassLoader());
List<LogRecord> logRecords = captureHandler.getLogRecords();
if (withLog) {
Assert.assertEquals(
logRecords.toString(), 1, logRecords.size());
LogRecord logRecord = logRecords.get(0);
Assert.assertEquals(
"Cancellation of orphaned noticeable futures is disabled " +
"because the JVM does not support phantom reference " +
"resurrection",
logRecord.getMessage());
Assert.assertSame(throwable, logRecord.getThrown());
}
else {
Assert.assertTrue(logRecords.isEmpty());
}
ReflectionUtilAdvice.setDeclaredFieldThrowable(null);
Assert.assertNull(
ReflectionTestUtil.getFieldValue(
AsyncBroker.class, "_REFERENT_FIELD"));
}
}
private static final String _KEY = "testKey";
private static final String _THREAD_ENABLED_KEY =
FinalizeManager.class.getName() + ".thread.enabled";
private static final String _VALUE = "testValue";
}