/** * 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.counter.service; import com.liferay.counter.kernel.model.Counter; import com.liferay.counter.kernel.service.CounterLocalServiceUtil; import com.liferay.portal.cache.key.SimpleCacheKeyGenerator; import com.liferay.portal.kernel.cache.key.CacheKeyGeneratorUtil; import com.liferay.portal.kernel.configuration.Filter; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.process.ClassPathUtil; import com.liferay.portal.kernel.process.ProcessCallable; import com.liferay.portal.kernel.process.ProcessChannel; import com.liferay.portal.kernel.process.ProcessConfig; import com.liferay.portal.kernel.process.ProcessConfig.Builder; import com.liferay.portal.kernel.process.ProcessException; import com.liferay.portal.kernel.process.ProcessExecutorUtil; import com.liferay.portal.kernel.test.rule.AggregateTestRule; import com.liferay.portal.kernel.test.rule.BaseTestRule; import com.liferay.portal.kernel.test.rule.callback.BaseTestCallback; import com.liferay.portal.kernel.util.PortalClassLoaderUtil; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.PropsUtil; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.test.rule.HypersonicServerTestRule; import com.liferay.portal.test.rule.LiferayIntegrationTestRule; import com.liferay.portal.util.InitUtil; import com.liferay.portal.util.PropsValues; import com.liferay.registry.BasicRegistryImpl; import com.liferay.registry.RegistryUtil; import java.io.File; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.Future; import javax.management.MBeanServer; import javax.management.ObjectName; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.Description; /** * @author Shuyang Zhou */ public class CounterLocalServiceTest { @ClassRule @Rule public static final AggregateTestRule aggregateTestRule = new AggregateTestRule( false, new LiferayIntegrationTestRule(), new BaseTestRule<>( new BaseTestCallback<Void, Void>() { @Override public void afterClass(Description description, Void v) { CounterLocalServiceUtil.reset(_COUNTER_NAME); } @Override public Void beforeClass(Description description) throws Exception { CounterLocalServiceUtil.reset(_COUNTER_NAME); Counter counter = CounterLocalServiceUtil.createCounter( _COUNTER_NAME); CounterLocalServiceUtil.updateCounter(counter); MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); // HikariCP for (ObjectName objectName : mBeanServer.queryNames( null, new ObjectName( "com.zaxxer.hikari:type=Pool (*"))) { mBeanServer.invoke( objectName, "softEvictConnections", null, null); } // Tomcat for (ObjectName objectName : mBeanServer.queryNames( null, new ObjectName( "TomcatJDBCPool:type=ConnectionPool," + "name=*"))) { mBeanServer.invoke(objectName, "purge", null, null); } return null; } }), HypersonicServerTestRule.INSTANCE); @Test public void testConcurrentIncrement() throws Exception { String classPath = getClassPath(); List<String> arguments = new ArrayList<>(); arguments.add("-Dliferay.mode=test"); arguments.add("-Dsun.zip.disableMemoryMapping=true"); for (String property : HypersonicServerTestRule.INSTANCE.getJdbcProperties()) { arguments.add("-D" + property); } arguments.add("-Xmx1024m"); arguments.add("-XX:MaxPermSize=200m"); Builder builder = new Builder(); builder.setArguments(arguments); builder.setBootstrapClassPath(classPath); builder.setReactClassLoader(PortalClassLoaderUtil.getClassLoader()); builder.setRuntimeClassPath(classPath); ProcessConfig processConfig = builder.build(); List<Future<Long[]>> futuresList = new ArrayList<>(); for (int i = 0; i < _PROCESS_COUNT; i++) { ProcessCallable<Long[]> processCallable = new IncrementProcessCallable( "Increment Process-" + i, _COUNTER_NAME, _INCREMENT_COUNT); ProcessChannel<Long[]> processChannel = ProcessExecutorUtil.execute( processConfig, processCallable); Future<Long[]> futures = processChannel.getProcessNoticeableFuture(); futuresList.add(futures); } int total = _PROCESS_COUNT * _INCREMENT_COUNT; List<Long> ids = new ArrayList<>(total); for (Future<Long[]> futures : futuresList) { Collections.addAll(ids, futures.get()); } Assert.assertEquals(ids.toString(), total, ids.size()); Collections.sort(ids); for (int i = 0; i < total; i++) { Long id = ids.get(i); Assert.assertEquals(i + 1, id.intValue()); } } protected String getClassPath() { String classPath = ClassPathUtil.getJVMClassPath(true); if (PropsValues.JDBC_DEFAULT_LIFERAY_POOL_PROVIDER.equals("hikaricp")) { StringBundler sb = new StringBundler(5); sb.append(classPath); sb.append(File.pathSeparator); sb.append(PropsValues.LIFERAY_LIB_PORTAL_DIR); sb.append(File.separator); sb.append( PropsUtil.get( PropsKeys.SETUP_LIFERAY_POOL_PROVIDER_JAR_NAME, new Filter("hikaricp"))); classPath = sb.toString(); } return classPath; } private static final String _COUNTER_NAME = StringUtil.randomString(); private static final int _INCREMENT_COUNT = 10000; private static final int _PROCESS_COUNT = 4; private static class IncrementProcessCallable implements ProcessCallable<Long[]> { public IncrementProcessCallable( String processName, String counterName, int incrementCount) { _processName = processName; _counterName = counterName; _incrementCount = incrementCount; } @Override public Long[] call() throws ProcessException { RegistryUtil.setRegistry(new BasicRegistryImpl()); System.setProperty( PropsKeys.COUNTER_INCREMENT + "." + _counterName, "1"); System.setProperty("catalina.base", "."); System.setProperty("external-properties", "portal-test.properties"); // C3PO System.setProperty("portal:jdbc.default.maxPoolSize", "1"); System.setProperty("portal:jdbc.default.minPoolSize", "0"); // HikariCP System.setProperty("portal:jdbc.default.maximumPoolSize", "1"); System.setProperty("portal:jdbc.default.minimumIdle", "0"); // Tomcat System.setProperty("portal:jdbc.default.initialSize", "0"); System.setProperty("portal:jdbc.default.maxActive", "1"); System.setProperty("portal:jdbc.default.maxIdle", "0"); System.setProperty("portal:jdbc.default.minIdle", "0"); CacheKeyGeneratorUtil cacheKeyGeneratorUtil = new CacheKeyGeneratorUtil(); cacheKeyGeneratorUtil.setDefaultCacheKeyGenerator( new SimpleCacheKeyGenerator()); InitUtil.initWithSpring( Arrays.asList( "META-INF/base-spring.xml", "META-INF/counter-spring.xml"), false, true); List<Long> ids = new ArrayList<>(); try { for (int i = 0; i < _incrementCount; i++) { ids.add(CounterLocalServiceUtil.increment(_counterName)); } } catch (SystemException se) { throw new ProcessException(se); } return ids.toArray(new Long[ids.size()]); } @Override public String toString() { return _processName; } private static final long serialVersionUID = 1L; private final String _counterName; private final int _incrementCount; private final String _processName; } }