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                            try {
314                                    completeLatch.await();
315                            }
316                            catch (InterruptedException ie) {
317                                    throw processException(ie);
318                            }
319    
320                            // Compete again
321    
322                            return _competeIncrement(counterRegister, size);
323                    }
324    
325                    // Winner thread
326    
327                    try {
328    
329                            // Double check
330    
331                            counterHolder = counterRegister.getCounterHolder();
332                            newValue = counterHolder.addAndGet(size);
333    
334                            if (newValue > counterHolder.getRangeMax()) {
335                                    CounterHolder newCounterHolder = _obtainIncrement(
336                                            counterRegister.getName(), counterRegister.getRangeSize(),
337                                            0);
338    
339                                    newValue = newCounterHolder.addAndGet(size);
340    
341                                    counterRegister.setCounterHolder(newCounterHolder);
342                            }
343                    }
344                    catch (Exception e) {
345                            throw processException(e);
346                    }
347                    finally {
348    
349                            // Winner thread opens the latch so that loser threads can continue
350    
351                            completeLatch.done();
352                    }
353    
354                    return newValue;
355            }
356    
357            private CounterHolder _obtainIncrement(
358                            String counterName, long range, long size)
359                    throws SystemException {
360    
361                    Session session = null;
362    
363                    try {
364                            session = openSession();
365    
366                            Counter counter = (Counter)session.get(
367                                    CounterImpl.class, counterName, LockMode.UPGRADE);
368    
369                            long newValue = counter.getCurrentId();
370    
371                            if (size > newValue) {
372                                    newValue = size;
373                            }
374    
375                            long rangeMax = newValue + range;
376    
377                            counter.setCurrentId(rangeMax);
378    
379                            CounterHolder counterHolder = new CounterHolder(newValue, rangeMax);
380    
381                            session.saveOrUpdate(counter);
382    
383                            session.flush();
384    
385                            return counterHolder;
386                    }
387                    catch (Exception e) {
388                            throw processException(e);
389                    }
390                    finally {
391                            closeSession(session);
392                    }
393            }
394    
395            private static final int _DEFAULT_CURRENT_ID = 0;
396    
397            private static final int _MINIMUM_INCREMENT_SIZE = 1;
398    
399            private static final String _NAME = Counter.class.getName();
400    
401            private static final String _SQL_INSERT =
402                    "insert into Counter(name, currentId) values (?, ?)";
403    
404            private static final String _SQL_SELECT_ID_BY_NAME =
405                    "select currentId from Counter where name = ?";
406    
407            private static final String _SQL_SELECT_NAMES =
408                    "select name from Counter order by name asc";
409    
410            private static final String _SQL_UPDATE_NAME_BY_NAME =
411                    "update Counter set name = ? where name = ?";
412    
413            private Map<String, CounterRegister> _counterRegisterMap =
414                    new ConcurrentHashMap<String, CounterRegister>();
415            private Map<String, Integer> _rangeSizeMap =
416                    new ConcurrentHashMap<String, Integer>();
417    
418    }