/** * 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.util; import com.liferay.portal.kernel.memory.FinalizeAction; import com.liferay.portal.kernel.memory.FinalizeManager; import com.liferay.portal.kernel.test.GCUtil; import com.liferay.portal.kernel.test.ReflectionTestUtil; import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor; import com.liferay.portal.kernel.test.rule.TimeoutTestRule; import com.liferay.registry.BasicRegistryImpl; import com.liferay.registry.Registry; import com.liferay.registry.RegistryUtil; import com.liferay.registry.ServiceRegistration; import com.liferay.registry.ServiceTracker; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; /** * @author Tina Tian */ public class ServiceProxyFactoryTest { @ClassRule public static final CodeCoverageAssertor codeCoverageAssertor = CodeCoverageAssertor.INSTANCE; @Before public void setUp() { RegistryUtil.setRegistry(new BasicRegistryImpl()); } @Test public void testBlockingProxy() throws Exception { _testBlockingProxy(false); } @Test public void testBlockingProxyWithProxyService() throws Exception { _testBlockingProxy(true); } @Test public void testCloseServiceTrackerFinalizeAction() throws Exception { TestServiceUtil testServiceUtil = new TestServiceUtil(); ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, testServiceUtil, "nonStaticField", null, false); FinalizeAction finalizeAction = null; Map<Reference<?>, FinalizeAction> finalizeActions = ReflectionTestUtil.getFieldValue( FinalizeManager.class, "_finalizeActions"); for (Map.Entry<Reference<?>, FinalizeAction> entry : finalizeActions.entrySet()) { Reference<?> reference = entry.getKey(); if (!(reference instanceof PhantomReference<?>)) { continue; } Object referent = ReflectionTestUtil.getFieldValue( reference, "referent"); if (referent != testServiceUtil) { continue; } finalizeAction = entry.getValue(); Class<?> clazz = finalizeAction.getClass(); Assert.assertEquals( "com.liferay.portal.kernel.util.ServiceProxyFactory$" + "CloseServiceTrackerFinalizeAction", clazz.getName()); break; } Assert.assertNotNull(finalizeAction); final AtomicBoolean atomicBoolean = new AtomicBoolean(); final ServiceTracker<TestService, TestService> serviceTracker = ReflectionTestUtil.getFieldValue(finalizeAction, "_serviceTracker"); ReflectionTestUtil.setFieldValue( finalizeAction, "_serviceTracker", ProxyUtil.newProxyInstance( FinalizeManager.class.getClassLoader(), new Class<?>[] {ServiceTracker.class}, new InvocationHandler() { @Override public Object invoke( Object proxy, Method method, Object[] args) throws Throwable { if (method.equals( ServiceTracker.class.getMethod("close"))) { atomicBoolean.set(true); } return method.invoke(serviceTracker, args); } })); testServiceUtil = null; GCUtil.gc(true); ReflectionTestUtil.invoke( FinalizeManager.class, "_pollingCleanup", new Class<?>[0]); Assert.assertTrue(atomicBoolean.get()); } @Test public void testMisc() throws Exception { // Test 1, wrong field try { ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, null, "wrongFieldName", null, false); Assert.fail(); } catch (Throwable throwable) { Assert.assertSame(NoSuchFieldException.class, throwable.getClass()); Assert.assertEquals("wrongFieldName", throwable.getMessage()); } try { TestServiceUtil testServiceUtil = new TestServiceUtil(); ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, testServiceUtil, "wrongFieldName", null, false); Assert.fail(); } catch (Throwable throwable) { Assert.assertSame(NoSuchFieldException.class, throwable.getClass()); Assert.assertEquals("wrongFieldName", throwable.getMessage()); } // Test 2, field is static try { TestServiceUtil testServiceUtil = new TestServiceUtil(); ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, testServiceUtil, "testService", null, false); Assert.fail(); } catch (Throwable throwable) { Assert.assertSame( IllegalArgumentException.class, throwable.getClass()); Field testServiceField = ReflectionUtil.getDeclaredField( TestServiceUtil.class, "testService"); Assert.assertEquals( testServiceField + " is static", throwable.getMessage()); } // Test 3, field is not static try { ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, "nonStaticField", false); Assert.fail(); } catch (Throwable throwable) { Assert.assertSame( IllegalArgumentException.class, throwable.getClass()); Field testServiceField = ReflectionUtil.getDeclaredField( TestServiceUtil.class, "nonStaticField"); Assert.assertEquals( testServiceField + " is not static", throwable.getMessage()); } // Test 4, field already set TestServiceUtil testServiceUtil = new TestServiceUtil(); TestService testService = new TestServiceImpl(); testServiceUtil.nonStaticField = testService; ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, testServiceUtil, "nonStaticField", null, false); Assert.assertSame(testService, testServiceUtil.nonStaticField); // Test 5, test constructor new ServiceProxyFactory(); } @Test public void testNonblockingProxy() throws Exception { _testNonBlockingProxy(false); } @Test public void testNonblockingProxyWithFilter() throws Exception { _testNonBlockingProxy(true); } @Test public void testNonblockingProxyWithInstanceField() throws Exception { TestServiceUtil testServiceUtil = new TestServiceUtil(); TestService testService = ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, testServiceUtil, "nonStaticField", null, false); _testNonBlockingProxy(false, testService, testServiceUtil); } @Test public void testNullDummyService() throws Exception { TestService testService = ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, "testService", false, true); Assert.assertNull(testService); Registry registry = RegistryUtil.getRegistry(); ServiceRegistration<TestService> serviceRegistration = registry.registerService(TestService.class, new TestServiceImpl()); TestService newTestService = TestServiceUtil.testService; Assert.assertEquals( _TEST_SERVICE_NAME, newTestService.getTestServiceName()); Assert.assertEquals( _TEST_SERVICE_ID, newTestService.getTestServiceId()); Assert.assertFalse(ProxyUtil.isProxyClass(newTestService.getClass())); Assert.assertSame(TestServiceImpl.class, newTestService.getClass()); serviceRegistration.unregister(); } @Rule public final TestRule testRule = TimeoutTestRule.INSTANCE; public static class TestServiceImpl implements TestService { @Override public long getTestServiceId() { return _TEST_SERVICE_ID; } @Override public String getTestServiceName() { return _TEST_SERVICE_NAME; } } public interface TestService { public long getTestServiceId(); public String getTestServiceName(); } private void _testBlockingProxy(boolean proxyService) throws Exception { final TestService testService = ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, "testService", true); Assert.assertTrue(ProxyUtil.isProxyClass(testService.getClass())); Assert.assertNotSame(TestServiceImpl.class, testService.getClass()); FutureTask<Void> futureTask = new FutureTask<>( new Callable<Void>() { @Override public Void call() { Assert.assertEquals( _TEST_SERVICE_NAME, testService.getTestServiceName()); Assert.assertEquals( _TEST_SERVICE_ID, testService.getTestServiceId()); TestService newTestService = TestServiceUtil.testService; if (proxyService) { Assert.assertTrue( ProxyUtil.isProxyClass(newTestService.getClass())); Assert.assertNotSame( TestServiceImpl.class, newTestService.getClass()); } else { Assert.assertFalse( ProxyUtil.isProxyClass(newTestService.getClass())); Assert.assertSame( TestServiceImpl.class, newTestService.getClass()); } return null; } }); Thread thread = new Thread(futureTask); thread.start(); _waitForBlocked(testService, thread); Registry registry = RegistryUtil.getRegistry(); ServiceRegistration<TestService> serviceRegistration = null; if (proxyService) { serviceRegistration = registry.registerService( TestService.class, (TestService)ProxyFactory.newInstance( TestService.class.getClassLoader(), new Class<?>[] {TestService.class}, TestServiceImpl.class.getName())); } else { serviceRegistration = registry.registerService( TestService.class, new TestServiceImpl()); } futureTask.get(); serviceRegistration.unregister(); } private void _testNonBlockingProxy(boolean filterEnabled) throws Exception { TestService testService = null; if (filterEnabled) { testService = ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, "testService", "(test.filter=true)", false); } else { testService = ServiceProxyFactory.newServiceTrackedInstance( TestService.class, TestServiceUtil.class, "testService", false); } _testNonBlockingProxy(filterEnabled, testService, null); } private void _testNonBlockingProxy( boolean filterEnabled, TestService testService, TestServiceUtil testServiceUtil) throws Exception { Assert.assertTrue(ProxyUtil.isProxyClass(testService.getClass())); Assert.assertNotSame(TestServiceImpl.class, testService.getClass()); Assert.assertEquals(0, testService.getTestServiceId()); Assert.assertEquals(null, testService.getTestServiceName()); Registry registry = RegistryUtil.getRegistry(); ServiceRegistration<TestService> serviceRegistration = null; if (filterEnabled) { serviceRegistration = registry.registerService( TestService.class, new TestServiceImpl(), Collections.singletonMap("test.filter", "true")); } else { serviceRegistration = registry.registerService( TestService.class, new TestServiceImpl()); } TestService newTestService = null; if (testServiceUtil == null) { newTestService = TestServiceUtil.testService; } else { newTestService = testServiceUtil.nonStaticField; } Assert.assertEquals( _TEST_SERVICE_NAME, newTestService.getTestServiceName()); Assert.assertEquals( _TEST_SERVICE_ID, newTestService.getTestServiceId()); Assert.assertFalse(ProxyUtil.isProxyClass(newTestService.getClass())); Assert.assertSame(TestServiceImpl.class, newTestService.getClass()); serviceRegistration.unregister(); } private void _waitForBlocked(TestService testService, Thread targetThread) { InvocationHandler invocationHandler = ProxyUtil.getInvocationHandler( testService); Lock lock = ReflectionTestUtil.getFieldValue( invocationHandler, "_lock"); Object sync = ReflectionTestUtil.getFieldValue(lock, "sync"); Condition condition = ReflectionTestUtil.getFieldValue( invocationHandler, "_realServiceSet"); while (true) { Collection<Thread> waitingThreads = null; lock.lock(); try { waitingThreads = ReflectionTestUtil.invoke( sync, "getWaitingThreads", new Class<?>[] { AbstractQueuedSynchronizer.ConditionObject.class }, condition); } finally { lock.unlock(); } if (waitingThreads.contains(targetThread)) { return; } } } private static final long _TEST_SERVICE_ID = 1234L; private static final String _TEST_SERVICE_NAME = "TestServiceName"; private static class TestServiceUtil { public static volatile TestService testService; public volatile TestService nonStaticField; } }