001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.counter.service.persistence;
016    
017    import com.liferay.counter.model.Counter;
018    import com.liferay.counter.model.CounterHolder;
019    import com.liferay.counter.model.CounterRegister;
020    import com.liferay.counter.model.impl.CounterImpl;
021    import com.liferay.portal.kernel.concurrent.CompeteLatch;
022    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
023    import com.liferay.portal.kernel.dao.orm.LockMode;
024    import com.liferay.portal.kernel.dao.orm.ObjectNotFoundException;
025    import com.liferay.portal.kernel.dao.orm.Session;
026    import com.liferay.portal.kernel.exception.SystemException;
027    import com.liferay.portal.kernel.util.GetterUtil;
028    import com.liferay.portal.kernel.util.PropsKeys;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.model.Dummy;
031    import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
032    import com.liferay.portal.util.PropsUtil;
033    import com.liferay.portal.util.PropsValues;
034    
035    import java.sql.Connection;
036    import java.sql.PreparedStatement;
037    import java.sql.ResultSet;
038    import java.sql.SQLException;
039    
040    import java.util.ArrayList;
041    import java.util.List;
042    import java.util.Map;
043    import java.util.concurrent.ConcurrentHashMap;
044    
045    /**
046     * @author Brian Wing Shun Chan
047     * @author Harry Mark
048     * @author Michael Young
049     * @author Shuyang Zhou
050     * @author Edward Han
051     */
052    public class CounterFinderImpl
053            extends BasePersistenceImpl<Dummy> implements CounterFinder {
054    
055            public List<String> getNames() throws SystemException {
056                    Connection connection = null;
057                    PreparedStatement preparedStatement = null;
058                    ResultSet resultSet = null;
059    
060                    try {
061                            connection = getConnection();
062    
063                            preparedStatement = connection.prepareStatement(_SQL_SELECT_NAMES);
064    
065                            resultSet = preparedStatement.executeQuery();
066    
067                            List<String> list = new ArrayList<String>();
068    
069                            while (resultSet.next()) {
070                                    list.add(resultSet.getString(1));
071                            }
072    
073                            return list;
074                    }
075                    catch (SQLException sqle) {
076                            throw processException(sqle);
077                    }
078                    finally {
079                            DataAccess.cleanUp(connection, preparedStatement, resultSet);
080                    }
081            }
082    
083            public long increment() throws SystemException {
084                    return increment(_NAME);
085            }
086    
087            public long increment(String name) throws SystemException {
088                    return increment(name, _MINIMUM_INCREMENT_SIZE);
089            }
090    
091            public long increment(String name, int size) throws SystemException {
092                    if (size < _MINIMUM_INCREMENT_SIZE) {
093                            size = _MINIMUM_INCREMENT_SIZE;
094                    }
095    
096                    CounterRegister counterRegister = getCounterRegister(name);
097    
098                    return _competeIncrement(counterRegister, size);
099            }
100    
101            public void rename(String oldName, String newName) throws SystemException {
102                    CounterRegister counterRegister = getCounterRegister(oldName);
103    
104                    synchronized (counterRegister) {
105                            if (_counterRegisterMap.containsKey(newName)) {
106                                    throw new SystemException(
107                                            "Cannot rename " + oldName + " to " + newName);
108                            }
109    
110                            Connection connection = null;
111                            PreparedStatement preparedStatement = null;
112    
113                            try {
114                                    connection = getConnection();
115    
116                                    preparedStatement = connection.prepareStatement(
117                                            _SQL_UPDATE_NAME_BY_NAME);
118    
119                                    preparedStatement.setString(1, newName);
120                                    preparedStatement.setString(2, oldName);
121    
122                                    preparedStatement.executeUpdate();
123                            }
124                            catch (ObjectNotFoundException onfe) {
125                            }
126                            catch (Exception e) {
127                                    throw processException(e);
128                            }
129                            finally {
130                                    DataAccess.cleanUp(connection, preparedStatement);
131                            }
132    
133                            counterRegister.setName(newName);
134    
135                            _counterRegisterMap.put(newName, counterRegister);
136                            _counterRegisterMap.remove(oldName);
137                    }
138            }
139    
140            public void reset(String name) throws SystemException {
141                    CounterRegister counterRegister = getCounterRegister(name);
142    
143                    synchronized (counterRegister) {
144                            Session session = null;
145    
146                            try {
147                                    session = openSession();
148    
149                                    Counter counter = (Counter)session.get(CounterImpl.class, name);
150    
151                                    session.delete(counter);
152    
153                                    session.flush();
154                            }
155                            catch (ObjectNotFoundException onfe) {
156                            }
157                            catch (Exception e) {
158                                    throw processException(e);
159                            }
160                            finally {
161                                    closeSession(session);
162                            }
163    
164                            _counterRegisterMap.remove(name);
165                    }
166            }
167    
168            public void reset(String name, long size) throws SystemException {
169                    CounterRegister counterRegister = createCounterRegister(name, size);
170    
171                    _counterRegisterMap.put(name, counterRegister);
172            }
173    
174            protected CounterRegister createCounterRegister(String name)
175                    throws SystemException {
176    
177                    return createCounterRegister(name, -1);
178            }
179    
180            protected CounterRegister createCounterRegister(String name, long size)
181                    throws SystemException {
182    
183                    long rangeMin = -1;
184                    int rangeSize = getRangeSize(name);
185    
186                    Connection connection = null;
187                    PreparedStatement preparedStatement = null;
188                    ResultSet resultSet = null;
189    
190                    try {
191                            connection = getConnection();
192    
193                            preparedStatement = connection.prepareStatement(
194                                    _SQL_SELECT_ID_BY_NAME);
195    
196                            preparedStatement.setString(1, name);
197    
198                            resultSet = preparedStatement.executeQuery();
199    
200                            if (!resultSet.next()) {
201                                    rangeMin = _DEFAULT_CURRENT_ID;
202    
203                                    if (size > rangeMin) {
204                                            rangeMin = size;
205                                    }
206    
207                                    resultSet.close();
208                                    preparedStatement.close();
209    
210                                    preparedStatement = connection.prepareStatement(_SQL_INSERT);
211    
212                                    preparedStatement.setString(1, name);
213                                    preparedStatement.setLong(2, rangeMin);
214    
215                                    preparedStatement.executeUpdate();
216                            }
217                    }
218                    catch (Exception e) {
219                            throw processException(e);
220                    }
221                    finally {
222                            DataAccess.cleanUp(connection, preparedStatement, resultSet);
223                    }
224    
225                    CounterHolder counterHolder = _obtainIncrement(name, rangeSize, size);
226    
227                    return new CounterRegister(name, counterHolder, rangeSize);
228            }
229    
230            protected Connection getConnection() throws SQLException {
231                    Connection connection = getDataSource().getConnection();
232    
233                    return connection;
234            }
235    
236            protected CounterRegister getCounterRegister(String name)
237                    throws SystemException {
238    
239                    CounterRegister counterRegister = _counterRegisterMap.get(name);
240    
241                    if (counterRegister != null) {
242                            return counterRegister;
243                    }
244                    else {
245                            synchronized (_counterRegisterMap) {
246    
247                                    // Double check
248    
249                                    counterRegister = _counterRegisterMap.get(name);
250    
251                                    if (counterRegister == null) {
252                                            counterRegister = createCounterRegister(name);
253    
254                                            _counterRegisterMap.put(name, counterRegister);
255                                    }
256    
257                                    return counterRegister;
258                            }
259                    }
260            }
261    
262            protected int getRangeSize(String name) {
263                    if (name.equals(_NAME)) {
264                            return PropsValues.COUNTER_INCREMENT;
265                    }
266    
267                    String incrementType = null;
268    
269                    int pos = name.indexOf(StringPool.POUND);
270    
271                    if (pos != -1) {
272                            incrementType = name.substring(0, pos);
273                    }
274                    else {
275                            incrementType = name;
276                    }
277    
278                    Integer rangeSize = _rangeSizeMap.get(incrementType);
279    
280                    if (rangeSize == null) {
281                            rangeSize = GetterUtil.getInteger(
282                                    PropsUtil.get(
283                                            PropsKeys.COUNTER_INCREMENT_PREFIX + incrementType),
284                                    PropsValues.COUNTER_INCREMENT);
285    
286                            _rangeSizeMap.put(incrementType, rangeSize);
287                    }
288    
289                    return rangeSize.intValue();
290            }
291    
292            private long _competeIncrement(CounterRegister counterRegister, int size)
293                    throws SystemException {
294    
295                    CounterHolder counterHolder = counterRegister.getCounterHolder();
296    
297                    // Try to use the fast path
298    
299                    long newValue = counterHolder.addAndGet(size);
300    
301                    if (newValue <= counterHolder.getRangeMax()) {
302                            return newValue;
303                    }
304    
305                    // Use the slow path
306    
307                    CompeteLatch completeLatch = counterRegister.getCompeteLatch();
308    
309                    if (!completeLatch.compete()) {
310    
311                            // Loser thread has to wait for the winner thread to finish its job
312    
313                            completeLatch.await();
314    
315                            // Compete again
316    
317                            return _competeIncrement(counterRegister, size);
318                    }
319    
320                    // Winner thread
321    
322                    try {
323    
324                            // Double check
325    
326                            counterHolder = counterRegister.getCounterHolder();
327                            newValue = counterHolder.addAndGet(size);
328    
329                            if (newValue > counterHolder.getRangeMax()) {
330                                    CounterHolder newCounterHolder = _obtainIncrement(
331                                            counterRegister.getName(), counterRegister.getRangeSize(),
332                                            0);
333    
334                                    newValue = newCounterHolder.addAndGet(size);
335    
336                                    counterRegister.setCounterHolder(newCounterHolder);
337                            }
338                    }
339                    catch (Exception e) {
340                            throw processException(e);
341                    }
342                    finally {
343    
344                            // Winner thread opens the latch so that loser threads can continue
345    
346                            completeLatch.done();
347                    }
348    
349                    return newValue;
350            }
351    
352            private CounterHolder _obtainIncrement(
353                            String counterName, long range, long size)
354                    throws SystemException {
355    
356                    Session session = null;
357    
358                    try {
359                            session = openSession();
360    
361                            Counter counter = (Counter)session.get(
362                                    CounterImpl.class, counterName, LockMode.UPGRADE);
363    
364                            long newValue = counter.getCurrentId();
365    
366                            if (size > newValue) {
367                                    newValue = size;
368                            }
369    
370                            long rangeMax = newValue + range;
371    
372                            counter.setCurrentId(rangeMax);
373    
374                            CounterHolder counterHolder = new CounterHolder(newValue, rangeMax);
375    
376                            session.saveOrUpdate(counter);
377    
378                            session.flush();
379    
380                            return counterHolder;
381                    }
382                    catch (Exception e) {
383                            throw processException(e);
384                    }
385                    finally {
386                            closeSession(session);
387                    }
388            }
389    
390            private static final int _DEFAULT_CURRENT_ID = 0;
391    
392            private static final int _MINIMUM_INCREMENT_SIZE = 1;
393    
394            private static final String _NAME = Counter.class.getName();
395    
396            private static final String _SQL_INSERT =
397                    "insert into Counter(name, currentId) values (?, ?)";
398    
399            private static final String _SQL_SELECT_ID_BY_NAME =
400                    "select currentId from Counter where name = ?";
401    
402            private static final String _SQL_SELECT_NAMES =
403                    "select name from Counter order by name asc";
404    
405            private static final String _SQL_UPDATE_NAME_BY_NAME =
406                    "update Counter set name = ? where name = ?";
407    
408            private Map<String, CounterRegister> _counterRegisterMap =
409                    new ConcurrentHashMap<String, CounterRegister>();
410            private Map<String, Integer> _rangeSizeMap =
411                    new ConcurrentHashMap<String, Integer>();
412    
413    }