Merge "Sonar Fixes - CadiAuthFilter.java"
[music.git] / src / main / java / org / onap / music / lockingservice / zookeeper / ZkStatelessLockService.java
1 /*
2  * ============LICENSE_START========================================== org.onap.music
3  * ===================================================================
4  * Copyright (c) 2017 AT&T Intellectual Property 
5  * ===================================================================
6  * Modifications Copyright (c) 2018 IBM.
7  * Modifications Copyright (c) 2019 Samsung.
8  * ===================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
10  * in compliance with the License. You may obtain a copy of the License at
11  * 
12  * http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software distributed under the License
15  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
16  * or implied. See the License for the specific language governing permissions and limitations under
17  * the License.
18  * 
19  * ============LICENSE_END=============================================
20  * ====================================================================
21  */
22
23 package org.onap.music.lockingservice.zookeeper;
24
25
26 import java.util.List;
27 import java.util.SortedSet;
28 import java.util.TreeSet;
29 import org.apache.zookeeper.CreateMode;
30 import org.apache.zookeeper.KeeperException;
31 import org.apache.zookeeper.KeeperException.NoNodeException;
32 import org.apache.zookeeper.ZooDefs;
33 import org.apache.zookeeper.ZooKeeper;
34 import org.apache.zookeeper.data.ACL;
35 import org.apache.zookeeper.data.Stat;
36 import org.onap.music.datastore.PreparedQueryObject;
37 import org.onap.music.eelf.logging.EELFLoggerDelegate;
38 import org.onap.music.eelf.logging.format.AppMessages;
39 import org.onap.music.eelf.logging.format.ErrorSeverity;
40 import org.onap.music.eelf.logging.format.ErrorTypes;
41 import org.onap.music.main.MusicCore;
42 import org.onap.music.main.MusicUtil;
43
44 import com.datastax.driver.core.DataType;
45
46 /**
47  * A <a href="package.html">protocol to implement an exclusive write lock or to elect a leader</a>.
48  * <p/>
49  * You invoke {@link #lock()} to start the process of grabbing the lock; you may get the lock then
50  * or it may be some time later.
51  * <p/>
52  * You can register a listener so that you are invoked when you get the lock; otherwise you can ask
53  * if you have the lock by calling {@link #isOwner()}
54  *
55  */
56 public class ZkStatelessLockService extends ProtocolSupport {
57     public ZkStatelessLockService(ZooKeeper zk) {
58         zookeeper = zk;
59     }
60
61     private static EELFLoggerDelegate logger =
62                     EELFLoggerDelegate.getLogger(ZkStatelessLockService.class);
63
64     protected void createLock(final String path, final byte[] data) {
65         final List<ACL> acl = ZooDefs.Ids.OPEN_ACL_UNSAFE;
66         try {
67             retryOperation(new ZooKeeperOperation() {
68                 public boolean execute() throws KeeperException, InterruptedException {
69                     zookeeper.create(path, data, acl, CreateMode.PERSISTENT);
70                     return true;
71                 }
72             });
73         }catch (InterruptedException e) {
74             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
75         }catch (KeeperException e) {
76             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
77         }
78     }
79
80     @Override
81     public void close() {
82         try {
83             zookeeper.close();
84         }catch (InterruptedException e) {
85             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
86         }
87     }
88
89     public void setNodeData(final String lockName, final byte[] data) {
90         try {
91             retryOperation(new ZooKeeperOperation() {
92                 public boolean execute() throws KeeperException, InterruptedException {
93                     zookeeper.getSessionId();
94                     zookeeper.setData("/" + lockName, data, -1);
95                     return true;
96                 }
97             });
98         }catch (InterruptedException e) {
99             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
100         }catch (KeeperException e) {
101             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
102         }
103
104     }
105
106     public byte[] getNodeData(final String lockName) {
107         try {
108             if (zookeeper.exists("/" + lockName, null) != null)
109                 return zookeeper.getData("/" + lockName, false, null);
110             else
111                 return null;
112
113         }catch (InterruptedException e) {
114             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
115         }catch (KeeperException e) {
116             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
117         }
118         return null;
119     }
120
121     public boolean checkIfLockExists(String lockName) {
122         boolean result = false;
123         try {
124             Stat stat = zookeeper.exists(lockName, false);
125             if (stat != null) {
126                 result = true;
127             }
128         }catch (InterruptedException e) {
129             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
130             Thread.currentThread().interrupt();
131         }catch (KeeperException e) {
132             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
133         }
134         return result;
135     }
136
137     public void createNode(String nodeName) {
138         ensurePathExists(nodeName);
139     }
140
141     public String createLockId(String dir) {
142         ensurePathExists(dir);
143         LockZooKeeperOperation zop = new LockZooKeeperOperation(dir);
144         try {
145             retryOperation(zop);
146         }catch (InterruptedException e) {
147             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
148             Thread.currentThread().interrupt();
149         }catch (KeeperException e) {
150             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
151         }
152         return zop.getId();
153     }
154
155     /**
156      * Attempts to acquire the exclusive write lock returning whether or not it was acquired. Note
157      * that the exclusive lock may be acquired some time later after this method has been invoked
158      * due to the current lock owner going away.
159      */
160     public synchronized boolean lock(String dir, String lockId)
161                     throws KeeperException, InterruptedException {
162         if (isClosed()) {
163             return false;
164         }
165         LockZooKeeperOperation zop = new LockZooKeeperOperation(dir, lockId);
166         return (Boolean) retryOperation(zop);
167     }
168
169     /**
170      * Removes the lock or associated znode if you no longer require the lock. this also removes
171      * your request in the queue for locking in case you do not already hold the lock.
172      * 
173      * @throws RuntimeException throws a runtime exception if it cannot connect to zookeeper.
174      * @throws NoNodeException 
175      */
176     public synchronized void unlock(String lockId) throws RuntimeException, KeeperException.NoNodeException {
177         final String id = lockId;
178         if (!isClosed() && id != null) {
179             try {
180                 ZooKeeperOperation zopdel = new ZooKeeperOperation() {
181                     public boolean execute() throws KeeperException, InterruptedException {
182                         zookeeper.delete(id, -1);
183                         return Boolean.TRUE;
184                     }
185                 };
186                 zopdel.execute();
187             } catch (InterruptedException e) {
188                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
189                 // set that we have been interrupted.
190                 Thread.currentThread().interrupt();
191             } catch (KeeperException.NoNodeException e) {
192                 // do nothing
193                 throw new KeeperException.NoNodeException("Lock doesn't exists. Release lock operation failed.");
194             } catch (KeeperException e) {
195                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
196                 throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e);
197             }
198         }
199     }
200
201     public synchronized String currentLockHolder(String mainLock) {
202         final String id = mainLock;
203         if (!isClosed() && id != null) {
204             List<String> names;
205             try {
206                 names = zookeeper.getChildren(id, false);
207                 if (names.isEmpty())
208                     return "";
209                 SortedSet<ZNodeName> sortedNames = new TreeSet<>();
210                 for (String name : names) {
211                     sortedNames.add(new ZNodeName(id + "/" + name));
212                 }
213                 return sortedNames.first().getName();
214             } catch (InterruptedException e) {
215                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
216                 // set that we have been interrupted.
217                 Thread.currentThread().interrupt();
218             } catch (KeeperException.NoNodeException e) {
219                 // do nothing
220             } catch (KeeperException e) {
221                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
222                 throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e);
223             }
224         }
225         return "No lock holder!";
226     }
227
228     public synchronized void deleteLock(String mainLock) {
229         final String id = mainLock;
230         if (!isClosed() && id != null) {
231             try {
232                 ZooKeeperOperation zopdel = new ZooKeeperOperation() {
233                     public boolean execute() throws KeeperException, InterruptedException {
234                         List<String> names = zookeeper.getChildren(id, false);
235                         for (String name : names) {
236                             zookeeper.delete(id + "/" + name, -1);
237                         }
238                         zookeeper.delete(id, -1);
239                         return Boolean.TRUE;
240                     }
241                 };
242                 zopdel.execute();
243             } catch (InterruptedException e) {
244                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
245                 // set that we have been interrupted.
246                 Thread.currentThread().interrupt();
247             } catch (KeeperException.NoNodeException e) {
248                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
249                 // do nothing
250             } catch (KeeperException e) {
251                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
252                 throw (RuntimeException) new RuntimeException(e.getMessage()).initCause(e);
253             }
254         }
255
256     }
257
258     /**
259      * a zoookeeper operation that is mainly responsible for all the magic required for locking.
260      */
261     private class LockZooKeeperOperation implements ZooKeeperOperation {
262
263         /**
264          * find if we have been created earlier if not create our node
265          * 
266          * @param prefix the prefix node
267          * @param zookeeper the zookeeper client
268          * @param dir the dir parent
269          * @throws KeeperException
270          * @throws InterruptedException
271          */
272         private String dir;
273         private String id = null;
274
275         public String getId() {
276             return id;
277         }
278
279         public LockZooKeeperOperation(String dir) {
280             this.dir = dir;
281         }
282
283         public LockZooKeeperOperation(String dir, String id) {
284             this.dir = dir;
285             this.id = id;
286         }
287
288         /**
289          * the command that is run and retried for actually obtaining the lock
290          * 
291          * @return if the command was successful or not
292          */
293         public boolean execute() throws KeeperException, InterruptedException {
294             do {
295                 if (id == null) {
296                     String prefix = "x-";
297                     byte[] data = {0x12, 0x34};
298                     id = zookeeper.create(dir + "/" + prefix, data, getAcl(),
299                                     CreateMode.PERSISTENT_SEQUENTIAL);
300
301                     if (logger.isDebugEnabled()) {
302                         logger.debug(EELFLoggerDelegate.debugLogger, "Created id: " + id);
303                     }
304                     if (id != null) {
305                         Stat stat = null;
306                         try {
307                             stat = zookeeper.exists(id, false);
308                         } catch (InterruptedException e) {
309                             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.EXECUTIONINTERRUPTED, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
310                             Thread.currentThread().interrupt();
311                         } catch (KeeperException e) {
312                             logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.KEEPERERROR, ErrorSeverity.ERROR, ErrorTypes.LOCKINGERROR);
313                         }
314
315                         if (stat != null){
316                             Long ctime = stat.getCtime();
317                             MusicUtil.zkNodeMap.put(id, ctime);
318                             PreparedQueryObject pQuery = new PreparedQueryObject();
319                             pQuery.appendQueryString(
320                                             "INSERT INTO admin.locks(lock_id, ctime) VALUES (?,?)");
321                             try {
322                                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), id));
323                                 pQuery.addValue(MusicUtil.convertToActualDataType(DataType.text(), ctime));
324                                 MusicCore.eventualPut(pQuery);
325                             } catch (Exception e) {
326                                 logger.error(EELFLoggerDelegate.errorLogger, e.getMessage(),AppMessages.UNKNOWNERROR, ErrorSeverity.ERROR, ErrorTypes.UNKNOWN);
327                             }
328                         }
329                         break;
330                    }
331                 }
332                 if (id != null) {
333                     List<String> names = zookeeper.getChildren(dir, false);
334                     if (names.isEmpty()) {
335                         logger.info(EELFLoggerDelegate.applicationLogger, "No children in: " + dir
336                                         + " when we've just " + "created one! Lets recreate it...");
337                         // lets force the recreation of the id
338                         id = null;
339                         return Boolean.FALSE;
340
341                     } else {
342                         // lets sort them explicitly (though they do seem to come back in order
343                         // ususally :)
344                         ZNodeName idName = new ZNodeName(id);
345                         SortedSet<ZNodeName> sortedNames = new TreeSet<>();
346                         for (String name : names) {
347                             sortedNames.add(new ZNodeName(dir + "/" + name));
348                         }
349                         if (!sortedNames.contains(idName))
350                             return Boolean.FALSE;
351
352                         SortedSet<ZNodeName> lessThanMe = sortedNames.headSet(idName);
353                         if (!lessThanMe.isEmpty()) {
354                             ZNodeName lastChildName = lessThanMe.last();
355                             String lastChildId = lastChildName.getName();
356                             if (logger.isDebugEnabled()) {
357                                 logger.debug(EELFLoggerDelegate.debugLogger, "watching less than me node: " + lastChildId);
358                             }
359                             Stat stat = zookeeper.exists(lastChildId, false);
360                             if (stat != null) {
361                                 return Boolean.FALSE;
362                             } else {
363                                 logger.info(EELFLoggerDelegate.applicationLogger,
364                                                 "Could not find the" + " stats for less than me: "
365                                                                 + lastChildName.getName());
366                             }
367                         } else
368                             return Boolean.TRUE;
369                     }
370                 }
371             } while (id == null);
372             return Boolean.FALSE;
373         }
374     }
375
376 }
377