/**
* 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.nio.intraband.messaging;
import com.liferay.portal.kernel.messaging.BaseDestination;
import com.liferay.portal.kernel.messaging.Message;
import com.liferay.portal.kernel.messaging.MessageBus;
import com.liferay.portal.kernel.messaging.MessageListener;
import com.liferay.portal.kernel.messaging.SynchronousDestination;
import com.liferay.portal.kernel.messaging.proxy.MessagingProxy;
import com.liferay.portal.kernel.nio.intraband.Datagram;
import com.liferay.portal.kernel.nio.intraband.RegistrationReference;
import com.liferay.portal.kernel.nio.intraband.test.MockIntraband;
import com.liferay.portal.kernel.nio.intraband.test.MockRegistrationReference;
import com.liferay.portal.kernel.process.local.LocalProcessLauncher;
import com.liferay.portal.kernel.resiliency.mpi.MPIHelperUtil;
import com.liferay.portal.kernel.resiliency.spi.MockSPI;
import com.liferay.portal.kernel.resiliency.spi.MockSPIProvider;
import com.liferay.portal.kernel.resiliency.spi.SPI;
import com.liferay.portal.kernel.resiliency.spi.SPIConfiguration;
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.test.rule.NewEnvTestRule;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.registry.BasicRegistryImpl;
import com.liferay.registry.Registry;
import com.liferay.registry.RegistryUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.rmi.RemoteException;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
/**
* @author Shuyang Zhou
*/
public class IntrabandBridgeDestinationTest {
@ClassRule
@Rule
public static final AggregateTestRule aggregateTestRule =
new AggregateTestRule(
CodeCoverageAssertor.INSTANCE, NewEnvTestRule.INSTANCE);
@Before
public void setUp() {
RegistryUtil.setRegistry(new BasicRegistryImpl());
Registry registry = RegistryUtil.getRegistry();
_messageBus = Mockito.mock(MessageBus.class);
registry.registerService(MessageBus.class, _messageBus);
_baseDestination = new SynchronousDestination();
_baseDestination.setName(
IntrabandBridgeDestinationTest.class.getName());
Mockito.when(
_messageBus.getDestination(_baseDestination.getName())
).thenReturn(
_baseDestination
);
_intrabandBridgeDestination = new IntrabandBridgeDestination(
_baseDestination);
_mockIntraband = new MockIntraband() {
@Override
protected Datagram processDatagram(Datagram datagram) {
ByteBuffer byteBuffer = datagram.getDataByteBuffer();
try {
MessageRoutingBag receivedMessageRoutingBag =
MessageRoutingBag.fromByteArray(byteBuffer.array());
Message receivedMessage =
receivedMessageRoutingBag.getMessage();
receivedMessage.put(_RECEIVE_KEY, _RECEIVE_VALUE);
return Datagram.createResponseDatagram(
datagram, receivedMessageRoutingBag.toByteArray());
}
catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
}
};
_mockRegistrationReference = new MockRegistrationReference(
_mockIntraband);
}
@Test
public void testSendMessage() throws ClassNotFoundException {
// Local message
final AtomicBoolean throwRuntimeException = new AtomicBoolean();
final AtomicReference<Message> messageReference =
new AtomicReference<>();
MessageListener messageListener = new MessageListener() {
@Override
public void receive(Message message) {
if (throwRuntimeException.get()) {
throw new RuntimeException();
}
messageReference.set(message);
}
};
_baseDestination.register(messageListener);
Message message = new Message();
message.put(MessagingProxy.LOCAL_MESSAGE, Boolean.TRUE);
_intrabandBridgeDestination.send(message);
Assert.assertNull(message.get(MessageRoutingBag.MESSAGE_ROUTING_BAG));
Assert.assertSame(message, messageReference.get());
// Automatically create message routing bag
message = new Message();
_intrabandBridgeDestination.send(message);
Assert.assertNotNull(
message.get(MessageRoutingBag.MESSAGE_ROUTING_BAG));
Assert.assertSame(message, messageReference.get());
// Existing message routing bag
MessageRoutingBag messageRoutingBag = _createMessageRoutingBag();
message = messageRoutingBag.getMessage();
message.put(MessageRoutingBag.MESSAGE_ROUTING_BAG, messageRoutingBag);
_intrabandBridgeDestination.send(message);
Assert.assertSame(
messageRoutingBag,
message.get(MessageRoutingBag.MESSAGE_ROUTING_BAG));
// Unserializable message
messageRoutingBag = _createMessageRoutingBag();
message = messageRoutingBag.getMessage();
message.put(MessageRoutingBag.MESSAGE_ROUTING_BAG, messageRoutingBag);
messageRoutingBag.getMessageData();
Thread currentThread = Thread.currentThread();
ClassLoader contextClassLoader = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(
new ClassLoader() {
@Override
public Class<?> loadClass(String name)
throws ClassNotFoundException {
if (name.equals(Message.class.getName())) {
throw new ClassNotFoundException();
}
return super.loadClass(name);
}
});
try {
_intrabandBridgeDestination.send(message);
Assert.fail();
}
catch (RuntimeException re) {
Throwable throwable = re.getCause();
Assert.assertSame(
ClassNotFoundException.class, throwable.getClass());
}
finally {
currentThread.setContextClassLoader(contextClassLoader);
}
// Throw runtime exception
throwRuntimeException.set(true);
try {
_intrabandBridgeDestination.send(new Message());
Assert.fail();
}
catch (RuntimeException re) {
Throwable throwable = re.getCause();
Assert.assertSame(RuntimeException.class, throwable.getClass());
}
}
@Test
public void testSendMessageBag1() {
// Is not SPI, without child SPI
_intrabandBridgeDestination.sendMessageRoutingBag(null);
Assert.assertSame(
_baseDestination,
_messageBus.getDestination(_baseDestination.getName()));
}
@Test
public void testSendMessageBag2() throws Exception {
// Is not SPI, with child SPI, not visited, unable to send
MockSPI mockSPI = _createMockSPI("SPIProvider", "SPI1");
_installSPIs(mockSPI);
IOException ioException = new IOException();
_mockIntraband.setIOException(ioException);
try {
MessageRoutingBag messageRoutingBag = _createMessageRoutingBag();
_intrabandBridgeDestination.sendMessageRoutingBag(
messageRoutingBag);
Assert.fail();
}
catch (RuntimeException re) {
Throwable throwable = re.getCause();
Assert.assertEquals(RuntimeException.class, throwable.getClass());
Assert.assertSame(ioException, throwable.getCause());
}
finally {
_mockIntraband.setIOException(null);
}
// Is not SPI, with child SPI, not visited, able to send
MessageRoutingBag messageRoutingBag = _createMessageRoutingBag();
_intrabandBridgeDestination.sendMessageRoutingBag(messageRoutingBag);
Message message = messageRoutingBag.getMessage();
Assert.assertEquals(_RECEIVE_VALUE, message.get(_RECEIVE_KEY));
// Is not SPI, with child SPI, visited
messageRoutingBag = _createMessageRoutingBag();
messageRoutingBag.appendRoutingId(_toRoutingId(mockSPI));
_intrabandBridgeDestination.sendMessageRoutingBag(messageRoutingBag);
message = messageRoutingBag.getMessage();
Assert.assertNull(message.get(_RECEIVE_KEY));
}
@NewEnv(type = NewEnv.Type.CLASSLOADER)
@Test
public void testSendMessageBag3() throws Exception {
// Is SPI, without child SPI, downcast
ConcurrentMap<String, Object> attributes =
LocalProcessLauncher.ProcessContext.getAttributes();
MockSPI mockSPI1 = _createMockSPI("SPIProvider", "SPI1");
attributes.put(SPI.SPI_INSTANCE_PUBLICATION_KEY, mockSPI1);
MessageRoutingBag messageRoutingBag = _createMessageRoutingBag();
messageRoutingBag.setRoutingDowncast(true);
_intrabandBridgeDestination.sendMessageRoutingBag(messageRoutingBag);
Assert.assertTrue(messageRoutingBag.isVisited(_toRoutingId(mockSPI1)));
Message message = messageRoutingBag.getMessage();
Assert.assertNull(message.get(_RECEIVE_KEY));
// Is SPI, without child SPI, upcast, unable to send
IOException ioException = new IOException();
_mockIntraband.setIOException(ioException);
try {
messageRoutingBag = _createMessageRoutingBag();
_intrabandBridgeDestination.sendMessageRoutingBag(
messageRoutingBag);
Assert.fail();
}
catch (RuntimeException re) {
Throwable throwable = re.getCause();
Assert.assertEquals(RuntimeException.class, throwable.getClass());
Assert.assertSame(ioException, throwable.getCause());
}
finally {
_mockIntraband.setIOException(null);
}
Assert.assertTrue(messageRoutingBag.isVisited(_toRoutingId(mockSPI1)));
// Is SPI, without child SPI, upcast, able to send
messageRoutingBag = _createMessageRoutingBag();
_intrabandBridgeDestination.sendMessageRoutingBag(messageRoutingBag);
Assert.assertTrue(messageRoutingBag.isVisited(_toRoutingId(mockSPI1)));
message = messageRoutingBag.getMessage();
Assert.assertEquals(_RECEIVE_VALUE, message.get(_RECEIVE_KEY));
// Is SPI, with child SPI, not visited, downcast
MockSPI mockSPI2 = _createMockSPI("SPIProvider", "SPI2");
_installSPIs(mockSPI2);
messageRoutingBag = _createMessageRoutingBag();
messageRoutingBag.setRoutingDowncast(true);
_intrabandBridgeDestination.sendMessageRoutingBag(messageRoutingBag);
Assert.assertTrue(messageRoutingBag.isVisited(_toRoutingId(mockSPI1)));
message = messageRoutingBag.getMessage();
Assert.assertEquals(_RECEIVE_VALUE, message.get(_RECEIVE_KEY));
// Is SPI, with child SPI, visited, downcast
messageRoutingBag = _createMessageRoutingBag();
messageRoutingBag.appendRoutingId(_toRoutingId(mockSPI2));
messageRoutingBag.setRoutingDowncast(true);
_intrabandBridgeDestination.sendMessageRoutingBag(messageRoutingBag);
Assert.assertTrue(messageRoutingBag.isVisited(_toRoutingId(mockSPI1)));
message = messageRoutingBag.getMessage();
Assert.assertNull(message.get(_RECEIVE_KEY));
}
private static void _installSPIs(SPI... spis) throws RemoteException {
Map<String, Object> spiProviderContainers =
ReflectionTestUtil.getFieldValue(
MPIHelperUtil.class, "_spiProviderContainers");
for (SPI spi : spis) {
MPIHelperUtil.registerSPIProvider(
new MockSPIProvider(spi.getSPIProviderName()));
Object spiProviderContainer = spiProviderContainers.get(
spi.getSPIProviderName());
Map<String, SPI> spiMap = ReflectionTestUtil.getFieldValue(
spiProviderContainer, "_spis");
SPIConfiguration spiConfiguration = spi.getSPIConfiguration();
spiMap.put(spiConfiguration.getSPIId(), spi);
}
}
private MessageRoutingBag _createMessageRoutingBag() {
Message message = new Message();
message.setDestinationName(
IntrabandBridgeMessageListenerTest.class.getName());
return new MessageRoutingBag(message, true);
}
private MockSPI _createMockSPI(String spiProviderName, String spiId) {
MockSPI mockSPI = new MockSPI() {
@Override
public RegistrationReference getRegistrationReference() {
return _mockRegistrationReference;
}
};
mockSPI.spiConfiguration = new SPIConfiguration(
spiId, null, -1, null, null, null, null);
mockSPI.spiProviderName = spiProviderName;
return mockSPI;
}
private String _toRoutingId(SPI spi) throws RemoteException {
String spiProviderName = spi.getSPIProviderName();
SPIConfiguration spiConfiguration = spi.getSPIConfiguration();
String spiId = spiConfiguration.getSPIId();
return spiProviderName.concat(StringPool.POUND).concat(spiId);
}
private static final String _RECEIVE_KEY = "RECEIVE_KEY";
private static final String _RECEIVE_VALUE = "RECEIVE_VALUE";
private BaseDestination _baseDestination;
private IntrabandBridgeDestination _intrabandBridgeDestination;
private MessageBus _messageBus;
private MockIntraband _mockIntraband;
private MockRegistrationReference _mockRegistrationReference;
}