1 package org.onap.ccsdk.features.lib.rlock;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.Collections;
8 import javax.sql.DataSource;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
14 * Implementation of the locking service, providing <i>distributed</i> locking functionality. It is
15 * done using a table in SQL Database as a single synchronization point. Hence, for this
16 * implementation, it is required that all participating threads in all participating applications
17 * access the same database instance (or a distributed database that looks like one database
21 * The following table is required in the database:
24 * <pre style="color:darkblue;">
25 * CREATE TABLE IF NOT EXISTS `resource_lock` (
26 * `resource_lock_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
27 * `resource_name` varchar(256),
28 * `lock_holder` varchar(100) NOT NULL,
29 * `lock_count` smallint(6) NOT NULL,
30 * `lock_time` datetime NOT NULL,
31 * `expiration_time` datetime NOT NULL,
32 * PRIMARY KEY (`resource_lock_id`),
33 * UNIQUE KEY `IX1_RESOURCE_LOCK` (`resource_name`)
37 * The implementation tries to insert records in the table for all the requested resources. If there
38 * are already records for any of the resources, it fails and then makes several more tries before
39 * giving up and throwing {@link ResourceLockedException}.
42 * The class has 2 configurable parameters:
44 * <li><tt><b>retryCount</b></tt>: the numbers of retries, when locking a resource, default 20</li>
45 * <li><tt><b>lockWait</b></tt>: the time between each retry (in seconds), default 5 seconds</li>
47 * The total time before locking fails would be <tt>retryCount * lockWait</tt> seconds.
51 * @see SynchronizedFunction
52 * @see ResourceLockedException
54 public class LockHelperImpl implements LockHelper {
56 private static final Logger log = LoggerFactory.getLogger(LockHelperImpl.class);
58 private int retryCount = 20;
59 private int lockWait = 5; // Seconds
61 private DataSource dataSource;
64 public void lock(String resourceName, String lockRequester, int lockTimeout /* Seconds */) {
65 lock(Collections.singleton(resourceName), lockRequester, lockTimeout);
69 public void unlock(String resourceName, boolean force) {
70 unlock(Collections.singleton(resourceName), force);
74 public void lock(Collection<String> resourceNameList, String lockRequester, int lockTimeout /* Seconds */) {
75 for (int i = 0; true; i++) {
77 tryLock(resourceNameList, lockRequester, lockTimeout);
78 log.info("Resources locked: " + resourceNameList);
80 } catch (ResourceLockedException e) {
85 Thread.sleep(lockWait * 1000L);
86 } catch (InterruptedException ex) {
93 public void unlock(Collection<String> lockNames, boolean force) {
94 if (lockNames == null || lockNames.size() == 0) {
98 try (ResourceLockDao resourceLockDao = new ResourceLockDao(dataSource)) {
100 for (String name : lockNames) {
101 ResourceLock l = resourceLockDao.getByResourceName(name);
103 if (force || l.lockCount == 1) {
104 resourceLockDao.delete(l.id);
106 resourceLockDao.decrementLockCount(l.id);
110 resourceLockDao.commit();
111 log.info("Resources unlocked: " + lockNames);
112 } catch (Exception e) {
113 resourceLockDao.rollback();
118 public void tryLock(Collection<String> resourceNameList, String lockRequester, int lockTimeout /* Seconds */) {
119 if (resourceNameList == null || resourceNameList.isEmpty()) {
123 lockRequester = generateLockRequester(lockRequester, 100);
125 // First check if all requested records are available to lock
127 Date now = new Date();
129 try (ResourceLockDao resourceLockDao = new ResourceLockDao(dataSource)) {
131 List<ResourceLock> dbLockList = new ArrayList<>();
132 List<String> insertLockNameList = new ArrayList<>();
133 for (String name : resourceNameList) {
134 ResourceLock l = resourceLockDao.getByResourceName(name);
136 boolean canLock = l == null || now.getTime() > l.expirationTime.getTime()
137 || lockRequester != null && lockRequester.equals(l.lockHolder) || l.lockCount <= 0;
139 throw new ResourceLockedException(l.resourceName, l.lockHolder, lockRequester);
143 if (now.getTime() > l.expirationTime.getTime() || l.lockCount <= 0) {
148 insertLockNameList.add(name);
152 // Update the lock info in DB
153 for (ResourceLock l : dbLockList) {
154 resourceLockDao.update(l.id, lockRequester, now, new Date(now.getTime() + lockTimeout * 1000),
158 // Insert records for those that are not yet there
159 for (String lockName : insertLockNameList) {
160 ResourceLock l = new ResourceLock();
161 l.resourceName = lockName;
162 l.lockHolder = lockRequester;
164 l.expirationTime = new Date(now.getTime() + lockTimeout * 1000);
168 resourceLockDao.add(l);
169 } catch (Exception e) {
170 throw new ResourceLockedException(l.resourceName, "unknown", lockRequester);
174 resourceLockDao.commit();
176 } catch (Exception e) {
177 resourceLockDao.rollback();
183 private static String generateLockRequester(String name, int maxLength) {
187 int l1 = name.length();
188 String tname = Thread.currentThread().getName();
189 int l2 = tname.length();
190 if (l1 + l2 + 1 > maxLength) {
191 int maxl1 = maxLength / 2;
193 name = name.substring(0, maxl1);
196 int maxl2 = maxLength - l1 - 1;
198 tname = tname.substring(0, 6) + "..." + tname.substring(l2 - maxl2 + 9);
201 return tname + '-' + name;
204 public void setRetryCount(int retryCount) {
205 this.retryCount = retryCount;
208 public void setLockWait(int lockWait) {
209 this.lockWait = lockWait;
212 public void setDataSource(DataSource dataSource) {
213 this.dataSource = dataSource;