/** * 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.mailbox; import com.liferay.portal.kernel.io.BigEndianCodec; import com.liferay.portal.kernel.nio.intraband.Datagram; import com.liferay.portal.kernel.nio.intraband.test.MockIntraband; import com.liferay.portal.kernel.nio.intraband.test.MockRegistrationReference; 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.util.PropsKeys; import com.liferay.portal.kernel.util.PropsUtilAdvice; import com.liferay.portal.kernel.util.ThreadUtil; import com.liferay.portal.test.rule.AdviseWith; import com.liferay.portal.test.rule.AspectJNewEnvTestRule; import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Constructor; import java.nio.ByteBuffer; import java.util.concurrent.BlockingQueue; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; /** * @author Shuyang Zhou */ @NewEnv(type = NewEnv.Type.JVM) public class MailboxUtilTest { @ClassRule @Rule public static final AggregateTestRule aggregateTestRule = new AggregateTestRule( CodeCoverageAssertor.INSTANCE, AspectJNewEnvTestRule.INSTANCE); @AdviseWith(adviceClasses = {PropsUtilAdvice.class}) @Test public void testConstructor() { new MailboxUtil(); } @AdviseWith(adviceClasses = PropsUtilAdvice.class) @Test public void testDepositMailWithReaperThreadDisabled() { PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_REAPER_THREAD_ENABLED, Boolean.FALSE.toString()); PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_STORAGE_LIFE, String.valueOf(0)); Assert.assertEquals(0, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Assert.assertEquals(1, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Assert.assertEquals(2, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Assert.assertEquals(3, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Thread reaperThread = null; for (Thread thread : ThreadUtil.getThreads()) { if (thread == null) { continue; } String name = thread.getName(); if (name.equals(MailboxUtil.class.getName())) { reaperThread = thread; break; } } Assert.assertNull(reaperThread); } @AdviseWith( adviceClasses = {PropsUtilAdvice.class, ReceiptStubAdvice.class} ) @Test public void testDepositMailWithReaperThreadEnabled() throws Exception { PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_REAPER_THREAD_ENABLED, Boolean.TRUE.toString()); PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_STORAGE_LIFE, String.valueOf(0)); Assert.assertEquals(0, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Assert.assertEquals(1, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Assert.assertEquals(2, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Assert.assertEquals(3, MailboxUtil.depositMail(ByteBuffer.allocate(0))); Thread reaperThread = null; for (Thread thread : ThreadUtil.getThreads()) { if ((thread != null) && thread.getName().equals(MailboxUtil.class.getName())) { reaperThread = thread; break; } } Assert.assertNotNull(reaperThread); reaperThread.interrupt(); while (reaperThread.isInterrupted()); Assert.assertTrue(reaperThread.isAlive()); BlockingQueue<Object> overdueMailQueue = ReflectionTestUtil.getFieldValue( MailboxUtil.class, "_overdueMailQueue"); while (!overdueMailQueue.isEmpty()); ReceiptStubAdvice._throwException = true; RecorderUncaughtExceptionHandler recorderUncaughtExceptionHandler = new RecorderUncaughtExceptionHandler(); reaperThread.setUncaughtExceptionHandler( recorderUncaughtExceptionHandler); overdueMailQueue.offer(createReceiptStub()); reaperThread.join(); Assert.assertSame( reaperThread, recorderUncaughtExceptionHandler._thread); Throwable throwable = recorderUncaughtExceptionHandler._throwable; Assert.assertSame(IllegalStateException.class, throwable.getClass()); Assert.assertFalse(reaperThread.isAlive()); } @AdviseWith(adviceClasses = {PropsUtilAdvice.class}) @Test public void testReceiveMailWithReaperThreadDisabled() { PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_REAPER_THREAD_ENABLED, Boolean.FALSE.toString()); PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_STORAGE_LIFE, String.valueOf(10000)); Assert.assertNull(MailboxUtil.receiveMail(0)); ByteBuffer byteBuffer1 = ByteBuffer.allocate(0); long receipt1 = MailboxUtil.depositMail(byteBuffer1); ByteBuffer byteBuffer2 = ByteBuffer.allocate(0); long receipt2 = MailboxUtil.depositMail(byteBuffer2); Assert.assertSame(byteBuffer2, MailboxUtil.receiveMail(receipt2)); Assert.assertSame(byteBuffer1, MailboxUtil.receiveMail(receipt1)); } @AdviseWith(adviceClasses = {PropsUtilAdvice.class}) @Test public void testReceiveMailWithReaperThreadEnabled() { PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_REAPER_THREAD_ENABLED, Boolean.TRUE.toString()); PropsUtilAdvice.setProps( PropsKeys.INTRABAND_MAILBOX_STORAGE_LIFE, String.valueOf(10000)); Assert.assertNull(MailboxUtil.receiveMail(0)); ByteBuffer byteBuffer1 = ByteBuffer.allocate(0); long receipt1 = MailboxUtil.depositMail(byteBuffer1); ByteBuffer byteBuffer2 = ByteBuffer.allocate(0); long receipt2 = MailboxUtil.depositMail(byteBuffer2); Assert.assertSame(byteBuffer2, MailboxUtil.receiveMail(receipt2)); Assert.assertSame(byteBuffer1, MailboxUtil.receiveMail(receipt1)); } @AdviseWith(adviceClasses = {PropsUtilAdvice.class}) @Test public void testSendMailFail() { MockIntraband mockIntraband = new MockIntraband(); IOException iOException = new IOException(); mockIntraband.setIOException(iOException); try { MailboxUtil.sendMail( new MockRegistrationReference(mockIntraband), ByteBuffer.allocate(0)); Assert.fail(); } catch (MailboxException me) { Assert.assertSame(iOException, me.getCause()); } } @AdviseWith(adviceClasses = {PropsUtilAdvice.class}) @Test public void testSendMailSuccess() throws MailboxException { final long receipt = 100; MockIntraband mockIntraband = new MockIntraband() { @Override protected Datagram processDatagram(Datagram datagram) { byte[] data = new byte[8]; BigEndianCodec.putLong(data, 0, receipt); return Datagram.createResponseDatagram( datagram, ByteBuffer.wrap(data)); } }; Assert.assertEquals( receipt, MailboxUtil.sendMail( new MockRegistrationReference(mockIntraband), ByteBuffer.allocate(0))); } @Aspect public static class ReceiptStubAdvice { @Around( "execution(public long com.liferay.portal.kernel.nio.intraband." + "mailbox.MailboxUtil$ReceiptStub.getReceipt())" ) public Object getReceipt(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { if (_throwException) { throw new IllegalStateException(); } return proceedingJoinPoint.proceed(); } private static volatile boolean _throwException; } protected Object createReceiptStub() throws Exception { String mailboxUtilClassName = MailboxUtil.class.getName(); Class<?> clazz = Class.forName( mailboxUtilClassName.concat("$ReceiptStub")); Constructor<?> constructor = clazz.getDeclaredConstructor(long.class); constructor.setAccessible(true); Object object = constructor.newInstance(0); Assert.assertEquals(0, object.hashCode()); return object; } private static class RecorderUncaughtExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread thread, Throwable throwable) { _thread = thread; _throwable = throwable; } private volatile Thread _thread; private volatile Throwable _throwable; } }