/**
* 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.resiliency.spi.provider;
import com.liferay.portal.kernel.concurrent.AsyncBroker;
import com.liferay.portal.kernel.concurrent.DefaultNoticeableFuture;
import com.liferay.portal.kernel.io.DummyOutputStream;
import com.liferay.portal.kernel.nio.intraband.blocking.ExecutorIntraband;
import com.liferay.portal.kernel.process.ProcessCallable;
import com.liferay.portal.kernel.process.ProcessChannel;
import com.liferay.portal.kernel.process.ProcessConfig;
import com.liferay.portal.kernel.process.ProcessException;
import com.liferay.portal.kernel.process.ProcessExecutor;
import com.liferay.portal.kernel.process.ProcessExecutorUtil;
import com.liferay.portal.kernel.process.local.LocalProcessChannel;
import com.liferay.portal.kernel.resiliency.PortalResiliencyException;
import com.liferay.portal.kernel.resiliency.mpi.MPIHelperUtil;
import com.liferay.portal.kernel.resiliency.spi.MockRemoteSPI;
import com.liferay.portal.kernel.resiliency.spi.MockSPI;
import com.liferay.portal.kernel.resiliency.spi.MockWelder;
import com.liferay.portal.kernel.resiliency.spi.SPI;
import com.liferay.portal.kernel.resiliency.spi.SPIConfiguration;
import com.liferay.portal.kernel.resiliency.spi.SPIRegistryUtil;
import com.liferay.portal.kernel.resiliency.spi.agent.MockSPIAgent;
import com.liferay.portal.kernel.resiliency.spi.agent.SPIAgentFactoryUtil;
import com.liferay.portal.kernel.resiliency.spi.remote.RemoteSPI;
import com.liferay.portal.kernel.resiliency.spi.remote.RemoteSPIProxy;
import com.liferay.portal.kernel.test.CaptureHandler;
import com.liferay.portal.kernel.test.JDKLoggerTestUtil;
import com.liferay.portal.kernel.test.SyncThrowableThread;
import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.resiliency.spi.SPIRegistryImpl;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
/**
* @author Shuyang Zhou
*/
public class BaseSPIProviderTest {
@ClassRule
public static final CodeCoverageAssertor codeCoverageAssertor =
CodeCoverageAssertor.INSTANCE;
@Before
public void setUp() {
System.setProperty(
PropsKeys.INTRABAND_IMPL, ExecutorIntraband.class.getName());
System.setProperty(PropsKeys.INTRABAND_TIMEOUT_DEFAULT, "10000");
System.setProperty(
PropsKeys.INTRABAND_WELDER_IMPL, MockWelder.class.getName());
File currentDir = new File(".");
System.setProperty(
PropsKeys.LIFERAY_HOME, currentDir.getAbsolutePath());
SPIAgentFactoryUtil.registerSPIAgentClass(MockSPIAgent.class);
_testSPIProvider = new TestSPIProvider();
MPIHelperUtil.registerSPIProvider(_testSPIProvider);
ProcessExecutorUtil processExecutorUtil = new ProcessExecutorUtil();
processExecutorUtil.setProcessExecutor(_mockProcessExecutor);
SPIRegistryUtil spiRegistryUtil = new SPIRegistryUtil();
spiRegistryUtil.setSPIRegistry(new SPIRegistryImpl());
}
@Test
public void testCreateSPI() throws PortalResiliencyException {
try (CaptureHandler captureHandler =
JDKLoggerTestUtil.configureJDKLogger(
MPIHelperUtil.class.getName(), Level.OFF)) {
// Timeout
try {
_testSPIProvider.createSPI(_spiConfiguration);
}
catch (PortalResiliencyException pre) {
Assert.assertEquals(
"SPI synchronous queue waiting timeout. Forcibly " +
"cancelled SPI process launch.",
pre.getMessage());
Assert.assertNull(pre.getCause());
}
// Sucess
_mockProcessExecutor.setRegisterBack(true);
SPI spi = _testSPIProvider.createSPI(_spiConfiguration);
Assert.assertSame(RemoteSPIProxy.class, spi.getClass());
_mockProcessExecutor.sync();
// Reject
try {
_testSPIProvider.createSPI(_spiConfiguration);
}
catch (PortalResiliencyException pre) {
Assert.assertEquals(
"Unable to register SPI " + spi +
". Forcibly cancelled SPI process launch.",
pre.getMessage());
Assert.assertNull(pre.getCause());
}
// Interrupt
_mockProcessExecutor.setInterrupt(true);
_mockProcessExecutor.setRegisterBack(false);
try {
_testSPIProvider.createSPI(_spiConfiguration);
}
catch (PortalResiliencyException pre) {
Assert.assertEquals(
"Interrupted on waiting SPI process, registering back " +
"RMI stub",
pre.getMessage());
Throwable throwable = pre.getCause();
Assert.assertSame(
InterruptedException.class, throwable.getClass());
}
// Process executor failure
_mockProcessExecutor.setInterrupt(false);
_mockProcessExecutor.setRegisterBack(false);
_mockProcessExecutor.setThrowException(true);
try {
_testSPIProvider.createSPI(_spiConfiguration);
}
catch (PortalResiliencyException pre) {
Assert.assertEquals(
"Unable to launch SPI process", pre.getMessage());
Throwable throwable = pre.getCause();
Assert.assertSame(ProcessException.class, throwable.getClass());
Assert.assertEquals("ProcessException", throwable.getMessage());
}
}
}
private final MockProcessExecutor _mockProcessExecutor =
new MockProcessExecutor();
private final SPIConfiguration _spiConfiguration = new SPIConfiguration(
"testId", "java", "", MockSPIAgent.class.getName(), 8081, "",
new String[0], new String[0], 10, 10, 10, "");
private TestSPIProvider _testSPIProvider;
private static class MockProcessExecutor implements ProcessExecutor {
@Override
@SuppressWarnings("unchecked")
public <T extends Serializable> ProcessChannel<T> execute(
ProcessConfig processConfig, ProcessCallable<T> processCallable)
throws ProcessException {
if (_interrupt) {
Thread currentThread = Thread.currentThread();
currentThread.interrupt();
}
final MockSPI mockSPI = new MockSPI();
if (_registerBack) {
final String spiUUID = ((RemoteSPI)processCallable).getUUID();
_syncThrowableThread = new SyncThrowableThread<>(
new Callable<Void>() {
@Override
public Void call() throws InterruptedException {
SPISynchronousQueueUtil.notifySynchronousQueue(
spiUUID, mockSPI);
return null;
}
});
_syncThrowableThread.start();
}
if (_throwException) {
throw new ProcessException("ProcessException");
}
DefaultNoticeableFuture<T> defaultNoticeableFuture =
new DefaultNoticeableFuture<>();
defaultNoticeableFuture.set((T)mockSPI);
try {
return new LocalProcessChannel<>(
defaultNoticeableFuture,
new ObjectOutputStream(new DummyOutputStream()),
new AsyncBroker<Long, Serializable>());
}
catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
public void setInterrupt(boolean interrupt) {
_interrupt = interrupt;
}
public void setRegisterBack(boolean registerBack) {
_registerBack = registerBack;
}
public void setThrowException(boolean throwException) {
_throwException = throwException;
}
public void sync() {
_syncThrowableThread.sync();
_syncThrowableThread = null;
}
private boolean _interrupt;
private boolean _registerBack;
private SyncThrowableThread<Void> _syncThrowableThread;
private boolean _throwException;
}
private static class TestSPIProvider extends BaseSPIProvider {
@Override
public RemoteSPI createRemoteSPI(SPIConfiguration spiConfiguration) {
return new MockRemoteSPI(spiConfiguration) {
@Override
public String getUUID() {
return MockRemoteSPI.class.getName();
}
};
}
@Override
public String getClassPath() {
return StringPool.BLANK;
}
@Override
public String getName() {
return TestSPIProvider.class.getName();
}
}
}