/** * 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.fabric.netty.rpc; import com.liferay.portal.fabric.netty.NettyTestUtil; import com.liferay.portal.kernel.concurrent.DefaultNoticeableFuture; import com.liferay.portal.kernel.concurrent.NoticeableFuture; import com.liferay.portal.kernel.test.CaptureHandler; import com.liferay.portal.kernel.test.JDKLoggerTestUtil; import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.embedded.EmbeddedChannel; import java.nio.channels.ClosedChannelException; import java.util.List; import java.util.Queue; import java.util.logging.Level; import java.util.logging.LogRecord; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; /** * @author Shuyang Zhou */ public class RPCRequestTest { @ClassRule public static final CodeCoverageAssertor codeCoverageAssertor = CodeCoverageAssertor.INSTANCE; @Test public void testExecuteWithAsyncException() { RPCRequest<String> rpcRequest = new RPCRequest<>( _ID, new TestRPCCallable(null, false, _throwable, null)); RPCResponse<String> rpcResponse = new RPCResponse<>( _ID, false, null, _throwable); doTestExecute(rpcRequest, rpcResponse); } @Test public void testExecuteWithCancellation() { RPCRequest<String> rpcRequest = new RPCRequest<>( _ID, new TestRPCCallable(null, true, null, null)); RPCResponse<String> rpcResponse = new RPCResponse<>( _ID, true, null, null); doTestExecute(rpcRequest, rpcResponse); } @Test public void testExecuteWithResult() { RPCRequest<String> rpcRequest = new RPCRequest<>( _ID, new TestRPCCallable(null, false, null, _RESULT)); RPCResponse<String> rpcResponse = new RPCResponse<>( _ID, false, _RESULT, null); doTestExecute(rpcRequest, rpcResponse); } @Test public void testExecuteWithSyncException() { RPCRequest<String> rpcRequest = new RPCRequest<>( _ID, new TestRPCCallable(_throwable, false, null, null)); RPCResponse<String> rpcResponse = new RPCResponse<>( _ID, false, null, _throwable); doTestExecute(rpcRequest, rpcResponse); } @Test public void testSendRPCResponseCancelled() { ChannelPipeline channelPipeline = _embeddedChannel.pipeline(); channelPipeline.addLast( new ChannelOutboundHandlerAdapter() { @Override public void write( ChannelHandlerContext channelHandlerContext, Object object, ChannelPromise channelPromise) { channelPromise.cancel(true); } }); RPCRequest<String> rpcRequest = new RPCRequest<>( _ID, new TestRPCCallable(null, false, null, _RESULT)); RPCResponse<String> rpcResponse = new RPCResponse<>( _ID, true, null, null); try (CaptureHandler captureHandler = JDKLoggerTestUtil.configureJDKLogger( RPCRequest.class.getName(), Level.SEVERE)) { rpcRequest.sendRPCResponse(_embeddedChannel, rpcResponse); List<LogRecord> logRecords = captureHandler.getLogRecords(); LogRecord logRecord = logRecords.get(0); Assert.assertEquals( "Cancelled on sending RPC response: " + rpcResponse, logRecord.getMessage()); } } @Test public void testSendRPCResponseFailed() { _embeddedChannel.close(); RPCRequest<String> rpcRequest = new RPCRequest<>( _ID, new TestRPCCallable(null, false, null, _RESULT)); RPCResponse<String> rpcResponse = new RPCResponse<>( _ID, true, null, null); try (CaptureHandler captureHandler = JDKLoggerTestUtil.configureJDKLogger( RPCRequest.class.getName(), Level.SEVERE)) { rpcRequest.sendRPCResponse(_embeddedChannel, rpcResponse); List<LogRecord> logRecords = captureHandler.getLogRecords(); LogRecord logRecord = logRecords.get(0); Assert.assertEquals( "Unable to send RPC response: " + rpcResponse, logRecord.getMessage()); Throwable throwable = logRecord.getThrown(); Assert.assertTrue(throwable instanceof ClosedChannelException); } } @Test public void testToString() { RPCCallable<String> rpcCallable = new TestRPCCallable( null, true, null, null); RPCRequest<String> rpcRequest = new RPCRequest<>(_ID, rpcCallable); Assert.assertEquals( "{id=" + _ID + ", rpcCallable=" + rpcCallable.toString() + "}", rpcRequest.toString()); } protected void doTestExecute( RPCRequest<String> rpcRequest, RPCResponse<String> rpcResponse) { rpcRequest.execute(_embeddedChannel); Queue<Object> messages = _embeddedChannel.outboundMessages(); Assert.assertEquals(1, messages.size()); Object message = messages.poll(); Assert.assertTrue(message instanceof RPCResponse); Assert.assertEquals(rpcResponse.toString(), message.toString()); } private static final long _ID = System.currentTimeMillis(); private static final String _RESULT = "This is the result."; private final EmbeddedChannel _embeddedChannel = NettyTestUtil.createEmptyEmbeddedChannel(); private final Throwable _throwable = new Throwable( "This is the throwable."); private static class TestRPCCallable implements RPCCallable<String> { public TestRPCCallable( Throwable syncThrowable, boolean cancel, Throwable asyncThrowable, String result) { _syncThrowable = syncThrowable; _cancel = cancel; _asyncThrowable = asyncThrowable; _result = result; } @Override public NoticeableFuture<String> call() throws Throwable { if (_syncThrowable != null) { throw _syncThrowable; } DefaultNoticeableFuture<String> defaultNoticeableFuture = new DefaultNoticeableFuture<>(); if (_cancel) { defaultNoticeableFuture.cancel(true); } else if (_asyncThrowable != null) { defaultNoticeableFuture.setException(_asyncThrowable); } else { defaultNoticeableFuture.set(_result); } return defaultNoticeableFuture; } private static final long serialVersionUID = 1L; private final Throwable _asyncThrowable; private final boolean _cancel; private final String _result; private final Throwable _syncThrowable; } }