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