/** * 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.service; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.model.BaseModel; import com.liferay.portal.kernel.model.ResourceAction; import com.liferay.portal.kernel.model.ResourcePermission; import com.liferay.portal.kernel.model.Role; import com.liferay.portal.kernel.model.RoleConstants; import com.liferay.portal.kernel.service.ResourceActionLocalServiceUtil; import com.liferay.portal.kernel.service.ResourcePermissionLocalServiceUtil; import com.liferay.portal.kernel.service.RoleLocalServiceUtil; import com.liferay.portal.kernel.service.persistence.ResourcePermissionPersistence; import com.liferay.portal.kernel.test.ReflectionTestUtil; import com.liferay.portal.kernel.test.randomizerbumpers.UniqueStringRandomizerBumper; import com.liferay.portal.kernel.test.rule.AggregateTestRule; import com.liferay.portal.kernel.test.rule.DeleteAfterTestRun; import com.liferay.portal.kernel.test.util.RandomTestUtil; import com.liferay.portal.kernel.test.util.TestPropsValues; import com.liferay.portal.kernel.util.ProxyUtil; import com.liferay.portal.service.impl.ResourcePermissionLocalServiceImpl; import com.liferay.portal.service.impl.SynchronousInvocationHandler; import com.liferay.portal.service.test.ServiceTestUtil; import com.liferay.portal.spring.aop.ServiceBeanAopProxy; import com.liferay.portal.spring.transaction.DefaultTransactionExecutor; import com.liferay.portal.test.rule.ExpectedDBType; import com.liferay.portal.test.rule.ExpectedLog; import com.liferay.portal.test.rule.ExpectedLogs; import com.liferay.portal.test.rule.ExpectedMultipleLogs; import com.liferay.portal.test.rule.ExpectedType; import com.liferay.portal.test.rule.LiferayIntegrationTestRule; import com.liferay.portal.util.PropsValues; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import org.hibernate.util.JDBCExceptionReporter; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.AdvisedSupport; /** * @author Matthew Tambara * @author William Newbury * @author Shuyang Zhou */ public class ResourcePermissionLocalServiceConcurrentTest { @ClassRule @Rule public static final AggregateTestRule aggregateTestRule = new LiferayIntegrationTestRule(); @Before public void setUp() throws Exception { Assume.assumeTrue(PropsValues.RETRY_ADVICE_MAX_RETRIES != 0); _threadCount = ServiceTestUtil.THREAD_COUNT; if ((PropsValues.RETRY_ADVICE_MAX_RETRIES > 0) && (_threadCount > PropsValues.RETRY_ADVICE_MAX_RETRIES)) { _threadCount = PropsValues.RETRY_ADVICE_MAX_RETRIES; } _actionId = RandomTestUtil.randomString( UniqueStringRandomizerBumper.INSTANCE); _name = RandomTestUtil.randomString( UniqueStringRandomizerBumper.INSTANCE); _resourceAction = ResourceActionLocalServiceUtil.addResourceAction( _name, _actionId, RandomTestUtil.randomLong()); ResourceActionLocalServiceUtil.checkResourceActions(); AdvisedSupport advisedSupport = ServiceBeanAopProxy.getAdvisedSupport( ResourcePermissionLocalServiceUtil.getService()); TargetSource targetSource = advisedSupport.getTargetSource(); final ResourcePermissionLocalServiceImpl resourcePermissionLocalServiceImpl = (ResourcePermissionLocalServiceImpl)targetSource.getTarget(); final ResourcePermissionPersistence resourcePermissionPersistence = resourcePermissionLocalServiceImpl. getResourcePermissionPersistence(); ReflectionTestUtil.setFieldValue( resourcePermissionLocalServiceImpl, "resourcePermissionPersistence", ProxyUtil.newProxyInstance( ResourcePermissionPersistence.class.getClassLoader(), new Class<?>[] {ResourcePermissionPersistence.class}, new SynchronousInvocationHandler( _threadCount, new Runnable() { @Override public void run() { ReflectionTestUtil.setFieldValue( resourcePermissionLocalServiceImpl, "resourcePermissionPersistence", resourcePermissionPersistence); } }, ResourcePermissionPersistence.class.getMethod( "update", BaseModel.class), resourcePermissionPersistence))); } @ExpectedMultipleLogs( expectedMultipleLogs = { @ExpectedLogs( expectedLogs = { @ExpectedLog( expectedLog = "Application exception overridden by commit exception", expectedType = ExpectedType.PREFIX ) }, level = "ERROR", loggerClass = DefaultTransactionExecutor.class ), @ExpectedLogs( expectedLogs = { @ExpectedLog( expectedDBType = ExpectedDBType.DB2, expectedLog = "Batch failure", expectedType = ExpectedType.CONTAINS ), @ExpectedLog( expectedDBType = ExpectedDBType.DB2, expectedLog = "DB2 SQL Error: SQLCODE=-803", expectedType = ExpectedType.CONTAINS ), @ExpectedLog( expectedDBType = ExpectedDBType.HYPERSONIC, expectedLog = "integrity constraint violation", expectedType = ExpectedType.PREFIX ), @ExpectedLog( expectedDBType = ExpectedDBType.MYSQL, expectedLog = "Duplicate entry '", expectedType = ExpectedType.PREFIX ), @ExpectedLog( expectedDBType = ExpectedDBType.ORACLE, expectedLog = "ORA-00001: unique constraint", expectedType = ExpectedType.PREFIX ), @ExpectedLog( expectedDBType = ExpectedDBType.POSTGRESQL, expectedLog = "Batch entry", expectedType = ExpectedType.PREFIX ), @ExpectedLog( expectedDBType = ExpectedDBType.POSTGRESQL, expectedLog = "duplicate key", expectedType = ExpectedType.CONTAINS ), @ExpectedLog( expectedDBType = ExpectedDBType.SYBASE, expectedLog = "Attempt to insert duplicate key row", expectedType = ExpectedType.CONTAINS ) }, level = "ERROR", loggerClass = JDBCExceptionReporter.class ) } ) @Test public void testAddResourcePermissionConcurrently() throws Exception { SynchronousInvocationHandler.enable(); try { final String primKey = RandomTestUtil.randomString( UniqueStringRandomizerBumper.INSTANCE); Callable<ResourcePermission> callable = new Callable<ResourcePermission>() { @Override public ResourcePermission call() throws PortalException { Role role = RoleLocalServiceUtil.getRole( TestPropsValues.getCompanyId(), RoleConstants.GUEST); ResourcePermissionLocalServiceUtil. addResourcePermission( TestPropsValues.getCompanyId(), _name, 0, primKey, role.getRoleId(), _actionId); return ResourcePermissionLocalServiceUtil. fetchResourcePermission( TestPropsValues.getCompanyId(), _name, 0, primKey, role.getRoleId()); } }; List<FutureTask<ResourcePermission>> futureTasks = new ArrayList<>(); for (int i = 0; i < _threadCount; i++) { FutureTask<ResourcePermission> futureTask = new FutureTask<>( callable); Thread thread = new Thread( futureTask, ResourcePermissionLocalServiceConcurrentTest.class. getName() + "-concurrent-addResourcePermission-" + i); thread.start(); futureTasks.add(futureTask); } Set<ResourcePermission> resourcePermissions = new HashSet<>(); for (FutureTask<ResourcePermission> futureTask : futureTasks) { resourcePermissions.add(futureTask.get()); } Assert.assertEquals( resourcePermissions.toString(), 1, resourcePermissions.size()); Iterator<ResourcePermission> iterator = resourcePermissions.iterator(); _resourcePermission = iterator.next(); Assert.assertEquals( _resourceAction.getBitwiseValue(), _resourcePermission.getActionIds()); } finally { SynchronousInvocationHandler.disable(); } } private String _actionId; private String _name; @DeleteAfterTestRun private ResourceAction _resourceAction; @DeleteAfterTestRun private ResourcePermission _resourcePermission; private int _threadCount; }