/**
* 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.exportimport.lar.test;
import static com.liferay.exportimport.kernel.lifecycle.ExportImportLifecycleConstants.EVENT_PUBLICATION_LAYOUT_LOCAL_STARTED;
import static com.liferay.exportimport.kernel.lifecycle.ExportImportLifecycleConstants.EVENT_PUBLICATION_LAYOUT_LOCAL_SUCCEEDED;
import static com.liferay.exportimport.kernel.lifecycle.ExportImportLifecycleConstants.PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS;
import com.liferay.arquillian.extension.junit.bridge.junit.Arquillian;
import com.liferay.exportimport.kernel.lar.ExportImportProcessCallbackRegistryUtil;
import com.liferay.exportimport.kernel.lifecycle.ExportImportLifecycleManagerUtil;
import com.liferay.portal.kernel.test.rule.AggregateTestRule;
import com.liferay.portal.kernel.test.util.RandomTestUtil;
import com.liferay.portal.test.rule.LiferayIntegrationTestRule;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* @author Daniel Kocsis
*/
@RunWith(Arquillian.class)
public class ExportImportProcessCallbackRegistryTest {
@ClassRule
@Rule
public static final AggregateTestRule aggregateTestRule =
new LiferayIntegrationTestRule();
@Before
public void setUp() throws Exception {
_processId = RandomTestUtil.randomString();
}
@Test
public void testMultiThreadedParallelNestedProcessRegisterCallable()
throws Exception {
MockExportImportProcessCallable callable =
new MockExportImportProcessCallable();
startProcess(_processId);
Thread thread1 = new Thread(getFullProcessRunnable());
thread1.start();
Thread thread2 = new Thread(getFullProcessRunnable());
thread2.start();
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable);
Assert.assertFalse(callable.hasBeenCalled.get());
stopProcess(_processId);
Assert.assertTrue(callable.hasBeenCalled.get());
}
@Test
public void testMultiThreadedParallelProcessRegisterCallable()
throws Exception {
Thread thread1 = new Thread(getFullProcessRunnable());
Thread thread2 = new Thread(getFullProcessRunnable());
thread1.start();
thread2.start();
}
@Test
public void testMultiThreadedSynchronizedNestedProcessRegisterCallable()
throws Exception {
startProcess(_processId);
MockExportImportProcessCallable callable1 =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable1);
final CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = new Thread(getFullProcessRunnable(countDownLatch));
thread.start();
countDownLatch.await(1, TimeUnit.MINUTES);
Assert.assertFalse(callable1.hasBeenCalled.get());
stopProcess(_processId);
Assert.assertTrue(callable1.hasBeenCalled.get());
}
@Test
public void testMultiThreadedSynchronizedProcessRegisterCallable()
throws Exception {
MockExportImportProcessCallable callable =
new MockExportImportProcessCallable();
final CountDownLatch startedLatch = new CountDownLatch(1);
final CountDownLatch registeredLatch = new CountDownLatch(1);
final CountDownLatch stoppedLatch = new CountDownLatch(1);
Thread starterThread = new Thread(
() -> {
startProcess(_processId);
startedLatch.countDown();
});
Thread stopperThread = new Thread(
() -> {
stopProcess(_processId);
stoppedLatch.countDown();
});
Thread registrationThread = new Thread(
() -> {
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable);
registeredLatch.countDown();
});
starterThread.start();
startedLatch.await(1, TimeUnit.MINUTES);
Assert.assertFalse(callable.hasBeenCalled.get());
registrationThread.start();
registeredLatch.await(1, TimeUnit.MINUTES);
Assert.assertFalse(callable.hasBeenCalled.get());
stopperThread.start();
stoppedLatch.await(1, TimeUnit.MINUTES);
Assert.assertTrue(callable.hasBeenCalled.get());
}
@Test
public void testMultiThreadedSynchronizedSelfNestedProcessRegisterCallable()
throws Exception {
startProcess(_processId);
MockExportImportProcessCallable callable1 =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable1);
final CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = new Thread(
getFullProcessRunnable(_processId, countDownLatch));
thread.start();
countDownLatch.await(1, TimeUnit.MINUTES);
Assert.assertFalse(callable1.hasBeenCalled.get());
stopProcess(_processId);
Assert.assertTrue(callable1.hasBeenCalled.get());
}
@Test
public void testSingleThreadedNestedProcessRegisterCallable() {
startProcess(_processId);
MockExportImportProcessCallable callable =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable);
String nestedProcessId = RandomTestUtil.randomString();
startProcess(nestedProcessId);
MockExportImportProcessCallable callableNested =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
nestedProcessId, callableNested);
Assert.assertFalse(callableNested.hasBeenCalled.get());
stopProcess(nestedProcessId);
Assert.assertTrue(callableNested.hasBeenCalled.get());
Assert.assertFalse(callable.hasBeenCalled.get());
stopProcess(_processId);
Assert.assertTrue(callable.hasBeenCalled.get());
}
@Test
public void testSingleThreadedProcessRegisterMultipleCallables() {
startProcess(_processId);
MockExportImportProcessCallable callable1 =
new MockExportImportProcessCallable();
MockExportImportProcessCallable callable2 =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable1);
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable2);
Assert.assertFalse(callable1.hasBeenCalled.get());
Assert.assertFalse(callable2.hasBeenCalled.get());
stopProcess(_processId);
Assert.assertTrue(callable1.hasBeenCalled.get());
Assert.assertTrue(callable2.hasBeenCalled.get());
}
@Test
public void testSingleThreadedSelfNestedProcessRegisterCallable() {
startProcess(_processId);
MockExportImportProcessCallable callable =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callable);
startProcess(_processId);
MockExportImportProcessCallable callableNested =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
_processId, callableNested);
Assert.assertFalse(callableNested.hasBeenCalled.get());
stopProcess(_processId);
Assert.assertTrue(callableNested.hasBeenCalled.get());
Assert.assertFalse(callable.hasBeenCalled.get());
stopProcess(_processId);
Assert.assertTrue(callable.hasBeenCalled.get());
}
protected Runnable getFullProcessRunnable() {
return getFullProcessRunnable(RandomTestUtil.randomString(), null);
}
protected Runnable getFullProcessRunnable(CountDownLatch countDownLatch) {
return getFullProcessRunnable(
RandomTestUtil.randomString(), countDownLatch);
}
protected Runnable getFullProcessRunnable(
String processId, CountDownLatch countDownLatch) {
return () -> {
startProcess(processId);
MockExportImportProcessCallable callable =
new MockExportImportProcessCallable();
ExportImportProcessCallbackRegistryUtil.registerCallback(
processId, callable);
Assert.assertFalse(callable.hasBeenCalled.get());
stopProcess(processId);
Assert.assertTrue(callable.hasBeenCalled.get());
if (countDownLatch != null) {
countDownLatch.countDown();
}
};
}
protected void startProcess(String processId) {
ExportImportLifecycleManagerUtil.fireExportImportLifecycleEvent(
EVENT_PUBLICATION_LAYOUT_LOCAL_STARTED,
PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS, processId);
}
protected void stopProcess(String processId) {
ExportImportLifecycleManagerUtil.fireExportImportLifecycleEvent(
EVENT_PUBLICATION_LAYOUT_LOCAL_SUCCEEDED,
PROCESS_FLAG_LAYOUT_STAGING_IN_PROCESS, processId);
}
private String _processId;
private class MockExportImportProcessCallable implements Callable<Void> {
@Override
public Void call() throws Exception {
Assert.assertFalse(hasBeenCalled.get());
hasBeenCalled.set(Boolean.TRUE);
return null;
}
public void reset() {
hasBeenCalled.set(Boolean.FALSE);
}
public AtomicBoolean hasBeenCalled = new AtomicBoolean(false);
}
}