/**
* 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.service.PortletPreferencesLocalServiceUtil;
import com.liferay.portal.kernel.service.persistence.PortletPreferencesPersistence;
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.PortletPreferencesLocalServiceImpl;
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.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import javax.portlet.PortletPreferences;
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 Shuyang Zhou
*/
public class PortletPreferencesLocalServiceConcurrentTest {
@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;
}
AdvisedSupport advisedSupport = ServiceBeanAopProxy.getAdvisedSupport(
PortletPreferencesLocalServiceUtil.getService());
TargetSource targetSource = advisedSupport.getTargetSource();
final PortletPreferencesLocalServiceImpl
portletPreferencesLocalServiceImpl =
(PortletPreferencesLocalServiceImpl)targetSource.getTarget();
final PortletPreferencesPersistence portletPreferencesPersistence =
portletPreferencesLocalServiceImpl.
getPortletPreferencesPersistence();
ReflectionTestUtil.setFieldValue(
portletPreferencesLocalServiceImpl, "portletPreferencesPersistence",
ProxyUtil.newProxyInstance(
PortletPreferencesPersistence.class.getClassLoader(),
new Class<?>[] {PortletPreferencesPersistence.class},
new SynchronousInvocationHandler(
_threadCount,
new Runnable() {
@Override
public void run() {
ReflectionTestUtil.setFieldValue(
portletPreferencesLocalServiceImpl,
"portletPreferencesPersistence",
portletPreferencesPersistence);
}
},
PortletPreferencesPersistence.class.getMethod(
"update", BaseModel.class),
portletPreferencesPersistence)));
}
@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 testAddPortletPreferencesConcurrently() throws Exception {
SynchronousInvocationHandler.enable();
try {
final long ownerId = RandomTestUtil.randomLong();
final int ownerType = RandomTestUtil.randomInt();
final long plid = RandomTestUtil.randomLong();
final String portletId = RandomTestUtil.randomString(
UniqueStringRandomizerBumper.INSTANCE);
Callable<PortletPreferences> callable =
new Callable<PortletPreferences>() {
@Override
public PortletPreferences call() throws PortalException {
return
PortletPreferencesLocalServiceUtil.getPreferences(
TestPropsValues.getCompanyId(), ownerId,
ownerType, plid, portletId);
}
};
List<FutureTask<PortletPreferences>> futureTasks =
new ArrayList<>();
for (int i = 0; i < _threadCount; i++) {
FutureTask<PortletPreferences> futureTask = new FutureTask<>(
callable);
Thread thread = new Thread(
futureTask,
PortletPreferencesLocalServiceConcurrentTest.class.
getName() + "-concurrent-getPreferences-" + i);
thread.start();
futureTasks.add(futureTask);
}
Set<PortletPreferences> portletPreferencesSet = new HashSet<>();
for (FutureTask<PortletPreferences> futureTask : futureTasks) {
portletPreferencesSet.add(futureTask.get());
}
Assert.assertEquals(
portletPreferencesSet.toString(), 1,
portletPreferencesSet.size());
_portletPreferences =
PortletPreferencesLocalServiceUtil.getPortletPreferences(
ownerId, ownerType, plid, portletId);
}
finally {
SynchronousInvocationHandler.disable();
}
}
@DeleteAfterTestRun
private com.liferay.portal.kernel.model.PortletPreferences
_portletPreferences;
private int _threadCount;
}