/** * 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.handlers.NettyChannelAttributes; import com.liferay.portal.fabric.netty.rpc.handlers.NettyRPCChannelHandler; import com.liferay.portal.kernel.concurrent.AsyncBroker; import com.liferay.portal.kernel.concurrent.DefaultNoticeableFuture; import com.liferay.portal.kernel.concurrent.NoticeableFuture; import com.liferay.portal.kernel.process.ProcessException; import com.liferay.portal.kernel.test.CaptureHandler; import com.liferay.portal.kernel.test.JDKLoggerTestUtil; import com.liferay.portal.kernel.test.ReflectionTestUtil; import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor; import com.liferay.portal.kernel.util.StringPool; 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 io.netty.util.Attribute; import io.netty.util.AttributeKey; import java.io.Serializable; import java.nio.channels.ClosedChannelException; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; 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 RPCUtilTest { @ClassRule public static final CodeCoverageAssertor codeCoverageAssertor = new CodeCoverageAssertor() { @Override public void appendAssertClasses(List<Class<?>> assertClasses) { assertClasses.add(RPCSerializable.class); assertClasses.add(NettyRPCChannelHandler.class); } }; @Test public void testConstructor() { new RPCUtil(); } @Test public void testRPCWithCancellation() throws Exception { ChannelPipeline channelPipeline = _embeddedChannel.pipeline(); channelPipeline.addFirst( new ChannelOutboundHandlerAdapter() { @Override public void write( ChannelHandlerContext channelHandlerContext, Object object, ChannelPromise channelPromise) { channelPromise.cancel(true); } }); Future<String> future = RPCUtil.execute( _embeddedChannel, new ResultRPCCallable("result")); Assert.assertTrue(future.isCancelled()); } @Test public void testRPCWithException() throws Exception { // RPCResponse with exception ProcessException testException = new ProcessException("message"); Future<Serializable> future = RPCUtil.execute( _embeddedChannel, new ExceptionRPCCallable(testException)); _embeddedChannel.writeInbound(_embeddedChannel.readOutbound()); _embeddedChannel.writeInbound(_embeddedChannel.readOutbound()); try { future.get(); Assert.fail(); } catch (ExecutionException ee) { Throwable throwable = ee.getCause(); Assert.assertSame(testException, throwable); } // Channel closed failure, set back exception _embeddedChannel.close(); Future<String> channelFailureFuture = RPCUtil.execute( _embeddedChannel, new ResultRPCCallable(StringPool.BLANK)); try { channelFailureFuture.get(); Assert.fail(); } catch (ExecutionException ee) { Throwable throwable = ee.getCause(); Assert.assertSame( ClosedChannelException.class, throwable.getClass()); } // Channel closed failure, no match key Attribute<AsyncBroker<Long, String>> attribute = _embeddedChannel.attr( ReflectionTestUtil. <AttributeKey<AsyncBroker<Long, String>>>getFieldValue( NettyChannelAttributes.class, "_asyncBrokerKey")); final AtomicLong keyRef = new AtomicLong(); attribute.set( new AsyncBroker<Long, String>() { @Override public NoticeableFuture<String> post(Long key) { keyRef.set(key); return new DefaultNoticeableFuture<>(); } }); try (CaptureHandler captureHandler = JDKLoggerTestUtil.configureJDKLogger( RPCUtil.class.getName(), Level.SEVERE)) { RPCUtil.execute( _embeddedChannel, new ResultRPCCallable(StringPool.BLANK)); List<LogRecord> logRecords = captureHandler.getLogRecords(); Assert.assertEquals(logRecords.toString(), 1, logRecords.size()); LogRecord logRecord = logRecords.get(0); Assert.assertEquals( "Unable to place exception because no future exists with ID " + keyRef.get(), logRecord.getMessage()); Throwable throwable = logRecord.getThrown(); Assert.assertSame( ClosedChannelException.class, throwable.getClass()); } } @Test public void testRPCWithResult() throws Exception { String result = "result"; Future<String> future = RPCUtil.execute( _embeddedChannel, new ResultRPCCallable(result)); _embeddedChannel.writeInbound(_embeddedChannel.readOutbound()); _embeddedChannel.writeInbound(_embeddedChannel.readOutbound()); Assert.assertEquals(result, future.get()); } private final EmbeddedChannel _embeddedChannel = new EmbeddedChannel( NettyRPCChannelHandler.INSTANCE); private static class ExceptionRPCCallable implements RPCCallable<Serializable> { public ExceptionRPCCallable(Throwable throwable) { _throwable = throwable; } @Override public NoticeableFuture<Serializable> call() { DefaultNoticeableFuture<Serializable> defaultNoticeableFuture = new DefaultNoticeableFuture<>(); defaultNoticeableFuture.setException(_throwable); return defaultNoticeableFuture; } private static final long serialVersionUID = 1L; private final Throwable _throwable; } private static class ResultRPCCallable implements RPCCallable<String> { public ResultRPCCallable(String result) { _result = result; } @Override public NoticeableFuture<String> call() { DefaultNoticeableFuture<String> defaultNoticeableFuture = new DefaultNoticeableFuture<>(); defaultNoticeableFuture.set(_result); return defaultNoticeableFuture; } private static final long serialVersionUID = 1L; private final String _result; } }