Merge "Change recipe to operation to match type"
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / main / java / org / onap / policy / controlloop / actorserviceprovider / pipeline / ListenerManager.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.controlloop.actorserviceprovider.pipeline;
22
23 import java.util.ArrayList;
24 import java.util.IdentityHashMap;
25 import lombok.Getter;
26 import lombok.NoArgsConstructor;
27 import org.onap.policy.controlloop.actorserviceprovider.Util;
28
29 /**
30  * Listener manager, used by operations within the pipeline to determine if they should
31  * continue to run. When {@link #stop()} is called, the listeners are executed. The
32  * various methods synchronize on "this" while they manipulate internal data structures.
33  */
34 @NoArgsConstructor
35 public class ListenerManager {
36
37     @Getter
38     private volatile boolean running = true;
39
40     /**
41      * Listeners to be executed when {@link #stop()} is invoked.
42      */
43     private final IdentityHashMap<Runnable, Void> listeners = new IdentityHashMap<>(5);
44
45     /**
46      * Indicates that operations within the pipeline should stop executing.
47      */
48     public void stop() {
49         ArrayList<Runnable> items;
50
51         synchronized (this) {
52             if (!running) {
53                 return;
54             }
55
56             running = false;
57             items = new ArrayList<>(listeners.keySet());
58             listeners.clear();
59         }
60
61         items.forEach(this::runListener);
62     }
63
64     /**
65      * Adds a listener that is to be invoked when this controller is stopped. Note: if the
66      * controller is already stopped, then the listener will be invoked immediately,
67      * within the invoking thread.
68      *
69      * @param listener listener to be added
70      */
71     public void add(Runnable listener) {
72         if (!addOnly(listener)) {
73             runListener(listener);
74         }
75     }
76
77     /**
78      * Adds a listener that is to be invoked when this controller is stopped. Note: if the
79      * controller is already stopped, then the listener will be invoked immediately,
80      * within the invoking thread.
81      *
82      * @param listener listener to be added
83      * @return {@code true} if the the listener was added, {@code false} if it could not
84      *         be added because this manager has already been stopped
85      */
86     protected boolean addOnly(Runnable listener) {
87         synchronized (this) {
88             if (running) {
89                 listeners.put(listener, null);
90                 return true;
91             }
92         }
93
94         return false;
95     }
96
97     /**
98      * Runs a listener, catching any exceptions that it may throw.
99      *
100      * @param listener listener to be executed
101      */
102     protected void runListener(Runnable listener) {
103         // TODO do this asynchronously?
104         Util.logException(listener, "pipeline listener {} threw an exception", listener);
105     }
106
107     /**
108      * Removes a listener so that it is not invoked when this controller is stopped.
109      *
110      * @param listener listener to be removed
111      */
112     public synchronized void remove(Runnable listener) {
113         listeners.remove(listener);
114     }
115 }