1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.counter.service.persistence;
16  
17  import com.liferay.counter.model.Counter;
18  import com.liferay.counter.model.CounterHolder;
19  import com.liferay.counter.model.CounterRegister;
20  import com.liferay.portal.SystemException;
21  import com.liferay.portal.kernel.concurrent.CompeteLatch;
22  import com.liferay.portal.kernel.dao.jdbc.DataAccess;
23  import com.liferay.portal.kernel.dao.orm.ObjectNotFoundException;
24  import com.liferay.portal.kernel.util.GetterUtil;
25  import com.liferay.portal.kernel.util.PropsKeys;
26  import com.liferay.portal.kernel.util.StringPool;
27  import com.liferay.portal.model.Dummy;
28  import com.liferay.portal.service.persistence.impl.BasePersistenceImpl;
29  import com.liferay.portal.util.PropsUtil;
30  import com.liferay.portal.util.PropsValues;
31  
32  import java.sql.Connection;
33  import java.sql.PreparedStatement;
34  import java.sql.ResultSet;
35  import java.sql.SQLException;
36  
37  import java.util.ArrayList;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.concurrent.ConcurrentHashMap;
41  
42  /**
43   * <a href="CounterPersistenceImpl.java.html"><b><i>View Source</i></b></a>
44   *
45   * @author Brian Wing Shun Chan
46   * @author Harry Mark
47   * @author Michael Young
48   * @author Shuyang Zhou
49   */
50  public class CounterPersistenceImpl
51      extends BasePersistenceImpl<Dummy> implements CounterPersistence {
52  
53      public List<String> getNames() throws SystemException {
54          Connection connection = null;
55          PreparedStatement preparedStatement = null;
56          ResultSet resultSet = null;
57  
58          try {
59              connection = getConnection();
60  
61              preparedStatement = connection.prepareStatement(_SQL_SELECT_NAMES);
62  
63              resultSet = preparedStatement.executeQuery();
64  
65              List<String> list = new ArrayList<String>();
66  
67              while (resultSet.next()) {
68                  list.add(resultSet.getString(1));
69              }
70  
71              return list;
72          }
73          catch (SQLException sqle) {
74              throw processException(sqle);
75          }
76          finally {
77              DataAccess.cleanUp(connection, preparedStatement, resultSet);
78          }
79      }
80  
81      public long increment() throws SystemException {
82          return increment(_NAME);
83      }
84  
85      public long increment(String name) throws SystemException {
86          return increment(name, _MINIMUM_INCREMENT_SIZE);
87      }
88  
89      public long increment(String name, int size) throws SystemException {
90          if (size < _MINIMUM_INCREMENT_SIZE) {
91              size = _MINIMUM_INCREMENT_SIZE;
92          }
93  
94          CounterRegister counterRegister = getCounterRegister(name);
95  
96          return _competeIncrement(counterRegister, size);
97      }
98  
99      public void rename(String oldName, String newName) throws SystemException {
100         CounterRegister counterRegister = getCounterRegister(oldName);
101 
102         synchronized (counterRegister) {
103             if (_counterRegisterMap.containsKey(newName)) {
104                 throw new SystemException(
105                     "Cannot rename " + oldName + " to " + newName);
106             }
107 
108             Connection connection = null;
109             PreparedStatement preparedStatement = null;
110 
111             try {
112                 connection = getConnection();
113 
114                 preparedStatement = connection.prepareStatement(
115                     _SQL_UPDATE_NAME_BY_NAME);
116 
117                 preparedStatement.setString(1, newName);
118                 preparedStatement.setString(2, oldName);
119 
120                 preparedStatement.executeUpdate();
121             }
122             catch (ObjectNotFoundException onfe) {
123             }
124             catch (Exception e) {
125                 throw processException(e);
126             }
127             finally {
128                 DataAccess.cleanUp(connection, preparedStatement);
129             }
130 
131             counterRegister.setName(newName);
132 
133             _counterRegisterMap.put(newName, counterRegister);
134             _counterRegisterMap.remove(oldName);
135         }
136     }
137 
138     public void reset(String name) throws SystemException {
139         CounterRegister counterRegister = getCounterRegister(name);
140 
141         synchronized (counterRegister) {
142             Connection connection = null;
143             PreparedStatement preparedStatement = null;
144 
145             try {
146                 connection = getConnection();
147 
148                 preparedStatement = connection.prepareStatement(
149                     _SQL_DELETE_BY_NAME);
150 
151                 preparedStatement.setString(1, name);
152 
153                 preparedStatement.executeUpdate();
154             }
155             catch (ObjectNotFoundException onfe) {
156             }
157             catch (Exception e) {
158                 throw processException(e);
159             }
160             finally {
161                 DataAccess.cleanUp(connection, preparedStatement);
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         long rangeMax = -1;
185         int rangeSize = getRangeSize(name);
186 
187         Connection connection = null;
188         PreparedStatement preparedStatement = null;
189         ResultSet resultSet = null;
190 
191         try {
192             connection = getConnection();
193 
194             preparedStatement = connection.prepareStatement(
195                 _SQL_SELECT_ID_BY_NAME);
196 
197             preparedStatement.setString(1, name);
198 
199             resultSet = preparedStatement.executeQuery();
200 
201             if (resultSet.next()) {
202                 rangeMin = resultSet.getLong(1);
203 
204                 if (size > rangeMin) {
205                     rangeMin = size;
206                 }
207 
208                 rangeMax = rangeMin + rangeSize;
209 
210                 resultSet.close();
211                 preparedStatement.close();
212 
213                 preparedStatement = connection.prepareStatement(
214                     _SQL_UPDATE_ID_BY_NAME);
215 
216                 preparedStatement.setLong(1, rangeMax);
217                 preparedStatement.setString(2, name);
218             }
219             else {
220                 rangeMin = _DEFAULT_CURRENT_ID;
221 
222                 if (size > rangeMin) {
223                     rangeMin = size;
224                 }
225 
226                 rangeMax = rangeMin + rangeSize;
227 
228                 resultSet.close();
229                 preparedStatement.close();
230 
231                 preparedStatement = connection.prepareStatement(_SQL_INSERT);
232 
233                 preparedStatement.setString(1, name);
234                 preparedStatement.setLong(2, rangeMax);
235             }
236 
237             preparedStatement.executeUpdate();
238         }
239         catch (Exception e) {
240             throw processException(e);
241         }
242         finally {
243             DataAccess.cleanUp(connection, preparedStatement, resultSet);
244         }
245 
246         return new CounterRegister(name, rangeMin, rangeMax, rangeSize);
247     }
248 
249     protected Connection getConnection() throws SQLException {
250         Connection connection = getDataSource().getConnection();
251 
252         return connection;
253     }
254 
255     protected CounterRegister getCounterRegister(String name)
256         throws SystemException {
257 
258         CounterRegister counterRegister = _counterRegisterMap.get(name);
259 
260         if (counterRegister != null) {
261             return counterRegister;
262         }
263         else {
264             synchronized (_counterRegisterMap) {
265 
266                 // Double check
267 
268                 counterRegister = _counterRegisterMap.get(name);
269 
270                 if (counterRegister == null) {
271                     counterRegister = createCounterRegister(name);
272 
273                     _counterRegisterMap.put(name, counterRegister);
274                 }
275 
276                 return counterRegister;
277             }
278         }
279     }
280 
281     protected int getRangeSize(String name) {
282         if (name.equals(_NAME)) {
283             return PropsValues.COUNTER_INCREMENT;
284         }
285 
286         String incrementType = null;
287 
288         int pos = name.indexOf(StringPool.POUND);
289 
290         if (pos != -1) {
291             incrementType = name.substring(0, pos);
292         }
293         else {
294             incrementType = name;
295         }
296 
297         Integer rangeSize = _rangeSizeMap.get(incrementType);
298 
299         if (rangeSize == null) {
300             rangeSize = GetterUtil.getInteger(
301                 PropsUtil.get(
302                     PropsKeys.COUNTER_INCREMENT_PREFIX + incrementType),
303                 PropsValues.COUNTER_INCREMENT);
304 
305             _rangeSizeMap.put(incrementType, rangeSize);
306         }
307 
308         return rangeSize.intValue();
309     }
310 
311     private long _competeIncrement(CounterRegister counterRegister, int size)
312         throws SystemException {
313 
314         CounterHolder counterHolder = counterRegister.getCounterHolder();
315 
316         // Try to use the fast path
317 
318         long newValue = counterHolder.addAndGet(size);
319 
320         if (newValue <= counterHolder.getRangeMax()) {
321             return newValue;
322         }
323 
324         // Use the slow path
325 
326         CompeteLatch completeLatch = counterRegister.getCompeteLatch();
327 
328         if (!completeLatch.compete()) {
329 
330             // Loser thread has to wait for the winner thread to finish its job
331 
332             try {
333                 completeLatch.await();
334             }
335             catch (InterruptedException ie) {
336                 throw processException(ie);
337             }
338 
339             // Compete again
340 
341             return _competeIncrement(counterRegister, size);
342         }
343 
344         // Winner thread
345 
346         Connection connection = null;
347         PreparedStatement preparedStatement = null;
348         ResultSet resultSet = null;
349 
350         try {
351 
352             // Double check
353 
354             counterHolder = counterRegister.getCounterHolder();
355             newValue = counterHolder.addAndGet(size);
356 
357             if (newValue > counterHolder.getRangeMax()) {
358                 connection = getConnection();
359 
360                 preparedStatement = connection.prepareStatement(
361                     _SQL_SELECT_ID_BY_NAME);
362 
363                 preparedStatement.setString(1, counterRegister.getName());
364 
365                 resultSet = preparedStatement.executeQuery();
366 
367                 resultSet.next();
368 
369                 long currentId = resultSet.getLong(1);
370 
371                 newValue = currentId + 1;
372                 long rangeMax = currentId + counterRegister.getRangeSize();
373 
374                 resultSet.close();
375                 preparedStatement.close();
376 
377                 preparedStatement = connection.prepareStatement(
378                     _SQL_UPDATE_ID_BY_NAME);
379 
380                 preparedStatement.setLong(1, rangeMax);
381                 preparedStatement.setString(2, counterRegister.getName());
382 
383                 preparedStatement.executeUpdate();
384 
385                 counterRegister.setCounterHolder(
386                     new CounterHolder(newValue, rangeMax));
387             }
388         }
389         catch (Exception e) {
390             throw processException(e);
391         }
392         finally {
393             DataAccess.cleanUp(connection, preparedStatement, resultSet);
394 
395             // Winner thread opens the latch so that loser threads can continue
396 
397             completeLatch.done();
398         }
399 
400         return newValue;
401     }
402 
403     private static final int _DEFAULT_CURRENT_ID = 0;
404 
405     private static final int _MINIMUM_INCREMENT_SIZE = 1;
406 
407     private static final String _NAME = Counter.class.getName();
408 
409     private static final String _SQL_DELETE_BY_NAME =
410         "delete from Counter where name = ?";
411 
412     private static final String _SQL_INSERT =
413         "insert into Counter(name, currentId) values (?, ?)";
414 
415     private static final String _SQL_SELECT_ID_BY_NAME =
416         "select currentId from Counter where name = ?";
417 
418     private static final String _SQL_SELECT_NAMES =
419         "select name from Counter order by name asc";
420 
421     private static final String _SQL_UPDATE_ID_BY_NAME =
422         "update Counter set currentId = ? where name = ?";
423 
424     private static final String _SQL_UPDATE_NAME_BY_NAME =
425         "update Counter set name = ? where name = ?";
426 
427     private Map<String, CounterRegister> _counterRegisterMap =
428         new ConcurrentHashMap<String, CounterRegister>();
429     private Map<String, Integer> _rangeSizeMap =
430         new ConcurrentHashMap<String, Integer>();
431 
432 }