All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.liferay.counter.service.persistence.impl.CounterFinderImpl Maven / Gradle / Ivy

/**
 * 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.persistence.impl;

import com.liferay.counter.kernel.model.Counter;
import com.liferay.counter.kernel.service.persistence.CounterFinder;
import com.liferay.counter.model.CounterHolder;
import com.liferay.counter.model.CounterRegister;
import com.liferay.counter.model.impl.CounterImpl;
import com.liferay.petra.string.CharPool;
import com.liferay.petra.string.StringBundler;
import com.liferay.portal.kernel.cache.CacheRegistryItem;
import com.liferay.portal.kernel.concurrent.CompeteLatch;
import com.liferay.portal.kernel.dao.orm.LockMode;
import com.liferay.portal.kernel.dao.orm.ORMException;
import com.liferay.portal.kernel.dao.orm.ObjectNotFoundException;
import com.liferay.portal.kernel.dao.orm.Session;
import com.liferay.portal.kernel.dao.orm.SessionFactory;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.util.PropsUtil;
import com.liferay.portal.util.PropsValues;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

/**
 * @author Brian Wing Shun Chan
 * @author Harry Mark
 * @author Michael Young
 * @author Shuyang Zhou
 * @author Edward Han
 */
public class CounterFinderImpl implements CacheRegistryItem, CounterFinder {

	@Override
	public List getNames() {
		try (Connection connection = getConnection();
			PreparedStatement preparedStatement = connection.prepareStatement(
				_SQL_SELECT_NAMES);
			ResultSet resultSet = preparedStatement.executeQuery()) {

			List list = new ArrayList<>();

			while (resultSet.next()) {
				list.add(resultSet.getString(1));
			}

			return list;
		}
		catch (SQLException sqlException) {
			throw processException(sqlException);
		}
	}

	@Override
	public String getRegistryName() {
		return CounterFinderImpl.class.getName();
	}

	@Override
	public long increment() {
		return increment(_NAME);
	}

	@Override
	public long increment(String name) {
		return increment(name, _MINIMUM_INCREMENT_SIZE);
	}

	@Override
	public long increment(String name, int size) {
		if (size < _MINIMUM_INCREMENT_SIZE) {
			size = _MINIMUM_INCREMENT_SIZE;
		}

		return _competeIncrement(getCounterRegister(name), size);
	}

	@Override
	public void invalidate() {
		_counterRegisterMap.clear();
	}

	@Override
	public void rename(String oldName, String newName) {
		CounterRegister counterRegister = getCounterRegister(oldName);

		synchronized (counterRegister) {
			if (_counterRegisterMap.containsKey(newName)) {
				throw new SystemException(
					StringBundler.concat(
						"Cannot rename ", oldName, " to ", newName));
			}

			try (Connection connection = getConnection();
				PreparedStatement preparedStatement =
					connection.prepareStatement(_SQL_UPDATE_NAME_BY_NAME)) {

				preparedStatement.setString(1, newName);
				preparedStatement.setString(2, oldName);

				preparedStatement.executeUpdate();
			}
			catch (ObjectNotFoundException objectNotFoundException) {
				if (_log.isDebugEnabled()) {
					_log.debug(objectNotFoundException);
				}
			}
			catch (Exception exception) {
				throw processException(exception);
			}

			counterRegister.setName(newName);

			_counterRegisterMap.put(newName, counterRegister);
			_counterRegisterMap.remove(oldName);
		}
	}

	@Override
	public void reset(String name) {
		CounterRegister counterRegister = getCounterRegister(name);

		synchronized (counterRegister) {
			Session session = null;

			try {
				session = openSession();

				Counter counter = (Counter)session.get(CounterImpl.class, name);

				session.delete(counter);

				session.flush();
			}
			catch (ObjectNotFoundException objectNotFoundException) {
				if (_log.isDebugEnabled()) {
					_log.debug(objectNotFoundException);
				}
			}
			catch (Exception exception) {
				throw processException(exception);
			}
			finally {
				closeSession(session);
			}

			_counterRegisterMap.remove(name);
		}
	}

	@Override
	public void reset(String name, long size) {
		CounterRegister counterRegister = createCounterRegister(name, size);

		_counterRegisterMap.put(name, counterRegister);
	}

	protected void closeSession(Session session) throws ORMException {
		_sessionFactory.closeSession(session);
	}

	protected CounterRegister createCounterRegister(String name) {
		return createCounterRegister(name, -1);
	}

	protected CounterRegister createCounterRegister(String name, long size) {
		long rangeMin = -1;

		try (Connection connection = getConnection();
			PreparedStatement preparedStatement1 = connection.prepareStatement(
				_SQL_SELECT_ID_BY_NAME)) {

			preparedStatement1.setString(1, name);

			try (ResultSet resultSet = preparedStatement1.executeQuery()) {
				if (!resultSet.next()) {
					rangeMin = _DEFAULT_CURRENT_ID;

					if (size > rangeMin) {
						rangeMin = size;
					}

					try (PreparedStatement preparedStatement2 =
							connection.prepareStatement(_SQL_INSERT)) {

						preparedStatement2.setString(1, name);
						preparedStatement2.setLong(2, rangeMin);

						preparedStatement2.executeUpdate();
					}
				}
			}
		}
		catch (Exception exception) {
			throw processException(exception);
		}

		int rangeSize = getRangeSize(name);

		CounterHolder counterHolder = _obtainIncrement(name, rangeSize, size);

		return new CounterRegister(name, counterHolder, rangeSize);
	}

	protected Connection getConnection() throws SQLException {
		return _dataSource.getConnection();
	}

	protected CounterRegister getCounterRegister(String name) {
		CounterRegister counterRegister = _counterRegisterMap.get(name);

		if (counterRegister != null) {
			return counterRegister;
		}

		synchronized (_counterRegisterMap) {

			// Double check

			counterRegister = _counterRegisterMap.get(name);

			if (counterRegister == null) {
				counterRegister = createCounterRegister(name);

				_counterRegisterMap.put(name, counterRegister);
			}

			return counterRegister;
		}
	}

	protected int getRangeSize(String name) {
		if (name.equals(_NAME)) {
			return PropsValues.COUNTER_INCREMENT;
		}

		String incrementType = null;

		int pos = name.indexOf(CharPool.POUND);

		if (pos != -1) {
			incrementType = name.substring(0, pos);
		}
		else {
			incrementType = name;
		}

		Integer rangeSize = _rangeSizeMap.get(incrementType);

		if (rangeSize == null) {
			rangeSize = GetterUtil.getInteger(
				PropsUtil.get(
					PropsKeys.COUNTER_INCREMENT_PREFIX + incrementType),
				PropsValues.COUNTER_INCREMENT);

			_rangeSizeMap.put(incrementType, rangeSize);
		}

		return rangeSize.intValue();
	}

	protected Session openSession() throws ORMException {
		return _sessionFactory.openSession();
	}

	protected SystemException processException(Exception exception) {
		if (!(exception instanceof ORMException)) {
			_log.error("Caught unexpected exception", exception);
		}
		else if (_log.isDebugEnabled()) {
			_log.debug(exception);
		}

		return new SystemException(exception);
	}

	protected void setDataSource(DataSource dataSource) {
		_dataSource = dataSource;
	}

	protected void setSessionFactory(SessionFactory sessionFactory) {
		_sessionFactory = sessionFactory;
	}

	private long _competeIncrement(CounterRegister counterRegister, int size) {
		CounterHolder counterHolder = counterRegister.getCounterHolder();

		// Try to use the fast path

		long newValue = counterHolder.addAndGet(size);

		if (newValue <= counterHolder.getRangeMax()) {
			return newValue;
		}

		// Use the slow path

		CompeteLatch competeLatch = counterRegister.getCompeteLatch();

		if (!competeLatch.compete()) {

			// Loser thread has to wait for the winner thread to finish its job

			try {
				competeLatch.await();
			}
			catch (InterruptedException interruptedException) {
				throw processException(interruptedException);
			}

			// Compete again

			return _competeIncrement(counterRegister, size);
		}

		// Winner thread

		try {

			// Double check

			counterHolder = counterRegister.getCounterHolder();

			newValue = counterHolder.addAndGet(size);

			if (newValue > counterHolder.getRangeMax()) {
				int range = counterRegister.getRangeSize();

				if (size > range) {
					range = size;
				}

				CounterHolder newCounterHolder = _obtainIncrement(
					counterRegister.getName(), range, 0);

				newValue = newCounterHolder.addAndGet(size);

				counterRegister.setCounterHolder(newCounterHolder);
			}
		}
		catch (Exception exception) {
			throw processException(exception);
		}
		finally {

			// Winner thread opens the latch so that loser threads can continue

			competeLatch.done();
		}

		return newValue;
	}

	private CounterHolder _obtainIncrement(
		String counterName, long range, long size) {

		Session session = null;

		try {
			session = openSession();

			Counter counter = (Counter)session.get(
				CounterImpl.class, counterName, LockMode.UPGRADE);

			long newValue = counter.getCurrentId();

			if (size > newValue) {
				newValue = size;
			}

			long rangeMax = newValue + range;

			counter.setCurrentId(rangeMax);

			CounterHolder counterHolder = new CounterHolder(newValue, rangeMax);

			session.saveOrUpdate(counter);

			session.flush();

			return counterHolder;
		}
		catch (Exception exception) {
			throw processException(exception);
		}
		finally {
			closeSession(session);
		}
	}

	private static final int _DEFAULT_CURRENT_ID = 0;

	private static final int _MINIMUM_INCREMENT_SIZE = 1;

	private static final String _NAME = Counter.class.getName();

	private static final String _SQL_INSERT =
		"insert into Counter(name, currentId) values (?, ?)";

	private static final String _SQL_SELECT_ID_BY_NAME =
		"select currentId from Counter where name = ?";

	private static final String _SQL_SELECT_NAMES =
		"select name from Counter order by name asc";

	private static final String _SQL_UPDATE_NAME_BY_NAME =
		"update Counter set name = ? where name = ?";

	private static final Log _log = LogFactoryUtil.getLog(
		CounterFinderImpl.class);

	private final Map _counterRegisterMap =
		new ConcurrentHashMap<>();
	private DataSource _dataSource;
	private final Map _rangeSizeMap =
		new ConcurrentHashMap<>();
	private SessionFactory _sessionFactory;

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy