2 * ============LICENSE_START========================================== org.onap.music
3 * =================================================================== Copyright (c) 2017 AT&T
4 * Intellectual Property ===================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
6 * in compliance with the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software distributed under the License
11 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12 * or implied. See the License for the specific language governing permissions and limitations under
15 * ============LICENSE_END=============================================
16 * ====================================================================
18 package org.onap.music.lockingservice;
21 import java.util.List;
22 import java.util.SortedSet;
23 import java.util.TreeSet;
24 import org.apache.zookeeper.CreateMode;
25 import org.apache.zookeeper.KeeperException;
26 import org.apache.zookeeper.ZooDefs;
27 import org.apache.zookeeper.ZooKeeper;
28 import org.apache.zookeeper.data.ACL;
29 import org.apache.zookeeper.data.Stat;
30 import org.onap.music.eelf.logging.EELFLoggerDelegate;
33 * A <a href="package.html">protocol to implement an exclusive write lock or to elect a leader</a>.
35 * You invoke {@link #lock()} to start the process of grabbing the lock; you may get the lock then
36 * or it may be some time later.
38 * You can register a listener so that you are invoked when you get the lock; otherwise you can ask
39 * if you have the lock by calling {@link #isOwner()}
42 public class ZkStatelessLockService extends ProtocolSupport {
43 public ZkStatelessLockService(ZooKeeper zk) {
47 private static EELFLoggerDelegate LOG =
48 EELFLoggerDelegate.getLogger(ZkStatelessLockService.class);
50 protected void createLock(final String path, final byte[] data) {
51 final List<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE;
53 retryOperation(new ZooKeeperOperation() {
54 public boolean execute() throws KeeperException, InterruptedException {
55 zookeeper.create(path, data, acl, CreateMode.PERSISTENT);
59 } catch (KeeperException e) {
60 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
61 } catch (InterruptedException e) {
62 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
69 } catch (InterruptedException e) {
70 LOG.error(EELFLoggerDelegate.errorLogger, e.getMessage());
74 public void setNodeData(final String lockName, final byte[] data) {
76 retryOperation(new ZooKeeperOperation() {
77 public boolean execute() throws KeeperException, InterruptedException {
78 zookeeper.getSessionId();
79 zookeeper.setData("/" + lockName, data, -1);
83 } catch (KeeperException e) {
84 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
85 } catch (InterruptedException e) {
86 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
91 public byte[] getNodeData(final String lockName) {
93 if (zookeeper.exists("/" + lockName, null) != null)
94 return zookeeper.getData("/" + lockName, false, null);
98 } catch (KeeperException | InterruptedException e) {
99 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
104 public boolean checkIfLockExists(String lockName) {
105 boolean result = false;
107 Stat stat = zookeeper.exists(lockName, false);
111 } catch (KeeperException | InterruptedException e) {
112 LOG.error(EELFLoggerDelegate.errorLogger, e.getMessage());
117 public void createNode(String nodeName) {
118 ensurePathExists(nodeName);
121 public String createLockId(String dir) {
122 ensurePathExists(dir);
123 LockZooKeeperOperation zop = new LockZooKeeperOperation(dir);
126 } catch (KeeperException | InterruptedException e) {
127 LOG.error(EELFLoggerDelegate.errorLogger, e.getMessage());
133 * Attempts to acquire the exclusive write lock returning whether or not it was acquired. Note
134 * that the exclusive lock may be acquired some time later after this method has been invoked
135 * due to the current lock owner going away.
137 public synchronized boolean lock(String dir, String lockId)
138 throws KeeperException, InterruptedException {
142 LockZooKeeperOperation zop = new LockZooKeeperOperation(dir, lockId);
143 return (Boolean) retryOperation(zop);
147 * Removes the lock or associated znode if you no longer require the lock. this also removes
148 * your request in the queue for locking in case you do not already hold the lock.
150 * @throws RuntimeException throws a runtime exception if it cannot connect to zookeeper.
152 public synchronized void unlock(String lockId) throws RuntimeException {
153 final String id = lockId;
154 if (!isClosed() && id != null) {
156 ZooKeeperOperation zopdel = new ZooKeeperOperation() {
157 public boolean execute() throws KeeperException, InterruptedException {
158 zookeeper.delete(id, -1);
163 } catch (InterruptedException e) {
164 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
165 // set that we have been interrupted.
166 Thread.currentThread().interrupt();
167 } catch (KeeperException.NoNodeException e) {
169 } catch (KeeperException e) {
170 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
171 throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e);
176 public synchronized String currentLockHolder(String mainLock) {
177 final String id = mainLock;
178 if (!isClosed() && id != null) {
181 names = zookeeper.getChildren(id, false);
184 SortedSet<ZNodeName> sortedNames = new TreeSet<ZNodeName>();
185 for (String name : names) {
186 sortedNames.add(new ZNodeName(id + "/" + name));
188 return sortedNames.first().getName();
189 } catch (InterruptedException e) {
190 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
191 // set that we have been interrupted.
192 Thread.currentThread().interrupt();
193 } catch (KeeperException.NoNodeException e) {
195 } catch (KeeperException e) {
196 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
197 throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e);
200 return "No lock holder!";
203 public synchronized void deleteLock(String mainLock) {
204 final String id = mainLock;
205 if (!isClosed() && id != null) {
207 ZooKeeperOperation zopdel = new ZooKeeperOperation() {
208 public boolean execute() throws KeeperException, InterruptedException {
209 List<String> names = zookeeper.getChildren(id, false);
210 for (String name : names) {
211 zookeeper.delete(id + "/" + name, -1);
213 zookeeper.delete(id, -1);
218 } catch (InterruptedException e) {
219 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
220 // set that we have been interrupted.
221 Thread.currentThread().interrupt();
222 } catch (KeeperException.NoNodeException e) {
224 } catch (KeeperException e) {
225 LOG.error(EELFLoggerDelegate.errorLogger, "Caught: " + e, e);
226 throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e);
233 * a zoookeeper operation that is mainly responsible for all the magic required for locking.
235 private class LockZooKeeperOperation implements ZooKeeperOperation {
238 * find if we have been created earlier if not create our node
240 * @param prefix the prefix node
241 * @param zookeeper the zookeeper client
242 * @param dir the dir parent
243 * @throws KeeperException
244 * @throws InterruptedException
247 private String id = null;
249 public String getId() {
253 public LockZooKeeperOperation(String dir) {
257 public LockZooKeeperOperation(String dir, String id) {
263 * the command that is run and retried for actually obtaining the lock
265 * @return if the command was successful or not
267 public boolean execute() throws KeeperException, InterruptedException {
270 String prefix = "x-";
271 byte[] data = {0x12, 0x34};
272 id = zookeeper.create(dir + "/" + prefix, data, getAcl(),
273 CreateMode.PERSISTENT_SEQUENTIAL);
275 if (LOG.isDebugEnabled()) {
276 LOG.debug("Created id: " + id);
282 List<String> names = zookeeper.getChildren(dir, false);
283 if (names.isEmpty()) {
284 LOG.info(EELFLoggerDelegate.applicationLogger, "No children in: " + dir
285 + " when we've just " + "created one! Lets recreate it...");
286 // lets force the recreation of the id
289 // lets sort them explicitly (though they do seem to come back in order
291 ZNodeName idName = new ZNodeName(id);
292 SortedSet<ZNodeName> sortedNames = new TreeSet<ZNodeName>();
293 for (String name : names) {
294 sortedNames.add(new ZNodeName(dir + "/" + name));
296 if (!sortedNames.contains(idName))
297 return Boolean.FALSE;
299 SortedSet<ZNodeName> lessThanMe = sortedNames.headSet(idName);
300 if (!lessThanMe.isEmpty()) {
301 ZNodeName lastChildName = lessThanMe.last();
302 String lastChildId = lastChildName.getName();
303 if (LOG.isDebugEnabled()) {
304 LOG.debug("watching less than me node: " + lastChildId);
306 Stat stat = zookeeper.exists(lastChildId, false);
308 return Boolean.FALSE;
310 LOG.info(EELFLoggerDelegate.applicationLogger,
311 "Could not find the" + " stats for less than me: "
312 + lastChildName.getName());
318 } while (id == null);
319 return Boolean.FALSE;