248a41be697b79f1a9e1117b3ee12279d31ef4d8
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2017-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.eventmanager;
22
23 import java.io.Serializable;
24 import java.util.Deque;
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.UUID;
28 import java.util.concurrent.CompletableFuture;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentLinkedDeque;
31 import java.util.concurrent.Executor;
32 import java.util.concurrent.ExecutorService;
33 import java.util.concurrent.ForkJoinPool;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.atomic.AtomicLong;
36 import lombok.AccessLevel;
37 import lombok.Getter;
38 import lombok.ToString;
39 import org.onap.policy.controlloop.ControlLoopException;
40 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
41 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
42 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
43 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManager;
44 import org.onap.policy.controlloop.ophistory.OperationHistoryDataManagerStub;
45 import org.onap.policy.controlloop.processor.ControlLoopProcessor;
46 import org.onap.policy.drools.core.lock.LockCallback;
47 import org.onap.policy.drools.system.PolicyEngineConstants;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 /**
52  * Manager for a single event. Once this has been created, the event can be retracted from
53  * working memory. Invoke {@link #isActive()} to determine if the manager is active (i.e.,
54  * hasn't been replicated from another server). When the manager is no longer needed,
55  * {@link #destroy()} should be invoked.
56  */
57 @ToString(onlyExplicitlyIncluded = true)
58 public class ControlLoopEventManager implements StepContext, Serializable {
59
60     private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventManager.class);
61     private static final long serialVersionUID = -1216568161322872641L;
62
63     /**
64      * Data manager used when the policy engine's guard.disabled property is "true".
65      */
66     private static final OperationHistoryDataManager STUB_DATA_MANAGER = new OperationHistoryDataManagerStub();
67
68     private static final String GUARD_DISABLED_PROPERTY = "guard.disabled";
69     private static final String EVENT_MANAGER_SERVICE_CONFIG = "event-manager";
70
71     /**
72      * Counts the number of these objects that have been created. This is used by junit
73      * tests.
74      */
75     private static final AtomicLong createCount = new AtomicLong(0);
76
77     /**
78      * {@code True} if this object was created by this JVM instance, {@code false}
79      * otherwise. This will be {@code false} if this object is reconstituted from a
80      * persistent store or by transfer from another server.
81      */
82     private transient boolean createdByThisJvmInstance;
83
84     @Getter
85     @ToString.Include
86     public final String closedLoopControlName;
87     @Getter
88     @ToString.Include
89     private final UUID requestId;
90
91     /**
92      * Time, in milliseconds, when the control loop will time out.
93      */
94     @Getter
95     private final long endTimeMs;
96
97     // fields extracted from the ControlLoopParams
98     @Getter
99     private final String policyName;
100     @Getter
101     private final String policyVersion;
102
103     /**
104      * Maps a target entity to its lock.
105      */
106     private final transient Map<String, LockData> target2lock = new HashMap<>();
107
108     @Getter(AccessLevel.PROTECTED)
109     private final ControlLoopProcessor processor;
110
111     /**
112      * Set of properties used while processing the event.
113      */
114     private Map<String, Serializable> properties = new ConcurrentHashMap<>();
115
116     /**
117      * Unprocessed outcomes from the operations. Outcomes are added to this each time the
118      * "start" or "complete" callback is invoked, typically by an operation running in a
119      * background thread, thus it must be thread safe.
120      */
121     @Getter
122     private final transient Deque<OperationOutcome> outcomes = new ConcurrentLinkedDeque<>();
123
124
125     /**
126      * Constructs the object.
127      *
128      * @param params control loop parameters
129      * @param requestId event request ID
130      * @throws ControlLoopException if the event is invalid or if a YAML processor cannot
131      *         be created
132      */
133     public ControlLoopEventManager(ControlLoopParams params, UUID requestId) throws ControlLoopException {
134
135         createCount.incrementAndGet();
136
137         this.createdByThisJvmInstance = true;
138         this.closedLoopControlName = params.getClosedLoopControlName();
139         this.requestId = requestId;
140         this.policyName = params.getPolicyName();
141         this.policyVersion = params.getPolicyVersion();
142         this.processor = new ControlLoopProcessor(params.getToscaPolicy());
143         this.endTimeMs = System.currentTimeMillis() + detmControlLoopTimeoutMs();
144     }
145
146     /**
147      * Gets the number of manager objects that have been created.
148      *
149      * @return the number of manager objects that have been created
150      */
151     public static long getCreateCount() {
152         return createCount.get();
153     }
154
155     /**
156      * Determines if the manager is still active.
157      *
158      * @return {@code true} if the manager is still active, {@code false} otherwise
159      */
160     public boolean isActive() {
161         return createdByThisJvmInstance;
162     }
163
164     /**
165      * Cancels the current operation and frees all locks.
166      */
167     public void destroy() {
168         if (isActive()) {
169             getBlockingExecutor().execute(this::freeAllLocks);
170         }
171     }
172
173     /**
174      * Frees all locks.
175      */
176     private void freeAllLocks() {
177         target2lock.values().forEach(LockData::free);
178     }
179
180     /**
181      * Determines the overall control loop timeout.
182      *
183      * @return the policy timeout, in milliseconds, if specified, a default timeout
184      *         otherwise
185      */
186     private long detmControlLoopTimeoutMs() {
187         // validation checks preclude null or 0 timeout values in the policy
188         Integer timeout = processor.getPolicy().getProperties().getTimeout();
189         return TimeUnit.MILLISECONDS.convert(timeout, TimeUnit.SECONDS);
190     }
191
192     /**
193      * Requests a lock. This requests the lock for the time that remains before the
194      * timeout expires. This avoids having to extend the lock.
195      *
196      * @param targetEntity entity to be locked
197      * @return a future that can be used to await the lock
198      */
199     @Override
200     public synchronized CompletableFuture<OperationOutcome> requestLock(String targetEntity) {
201
202         long remainingMs = endTimeMs - System.currentTimeMillis();
203         int remainingSec = 15 + Math.max(0, (int) TimeUnit.SECONDS.convert(remainingMs, TimeUnit.MILLISECONDS));
204
205         LockData data = target2lock.computeIfAbsent(targetEntity, key -> {
206             LockData data2 = new LockData(key, requestId);
207             makeLock(targetEntity, requestId.toString(), remainingSec, data2);
208
209             data2.addUnavailableCallback(this::onComplete);
210
211             return data2;
212         });
213
214         return data.getFuture();
215     }
216
217     public void onStart(OperationOutcome outcome) {
218         outcomes.add(outcome);
219     }
220
221     public void onComplete(OperationOutcome outcome) {
222         outcomes.add(outcome);
223     }
224
225     /**
226      * Determines if the context contains a property.
227      *
228      * @param name name of the property of interest
229      * @return {@code true} if the context contains the property, {@code false} otherwise
230      */
231     @Override
232     public boolean contains(String name) {
233         return properties.containsKey(name);
234     }
235
236     /**
237      * Gets a property, casting it to the desired type.
238      *
239      * @param <T> desired type
240      * @param name name of the property whose value is to be retrieved
241      * @return the property's value, or {@code null} if it does not yet have a value
242      */
243     @Override
244     @SuppressWarnings("unchecked")
245     public <T> T getProperty(String name) {
246         return (T) properties.get(name);
247     }
248
249     /**
250      * Sets a property's value.
251      *
252      * @param name property name
253      * @param value new property value
254      */
255     @Override
256     public void setProperty(String name, Serializable value) {
257         logger.info("set property {}={} manager={}", name, value, this);
258         properties.put(name, value);
259     }
260
261     /**
262      * Removes a property.
263      *
264      * @param name property name
265      */
266     @Override
267     public void removeProperty(String name) {
268         properties.remove(name);
269     }
270
271     /**
272      * Initializes various components, on demand.
273      */
274     private static class LazyInitData {
275         private static final OperationHistoryDataManager DATA_MANAGER;
276         private static final ActorService ACTOR_SERVICE;
277
278         static {
279             EventManagerServices services = new EventManagerServices(EVENT_MANAGER_SERVICE_CONFIG);
280             ACTOR_SERVICE = services.getActorService();
281             DATA_MANAGER = services.getDataManager();
282         }
283     }
284
285     // the following methods may be overridden by junit tests
286
287     public Executor getExecutor() {
288         return ForkJoinPool.commonPool();
289     }
290
291     protected ExecutorService getBlockingExecutor() {
292         return PolicyEngineConstants.getManager().getExecutorService();
293     }
294
295     protected void makeLock(String targetEntity, String requestId, int holdSec, LockCallback callback) {
296         PolicyEngineConstants.getManager().createLock(targetEntity, requestId, holdSec, callback, false);
297     }
298
299     public ActorService getActorService() {
300         return LazyInitData.ACTOR_SERVICE;
301     }
302
303     public OperationHistoryDataManager getDataManager() {
304         boolean guardDisabled = "true".equalsIgnoreCase(getEnvironmentProperty(GUARD_DISABLED_PROPERTY));
305         return (guardDisabled ? STUB_DATA_MANAGER : LazyInitData.DATA_MANAGER);
306     }
307
308     protected String getEnvironmentProperty(String propName) {
309         return PolicyEngineConstants.getManager().getEnvironmentProperty(propName);
310     }
311 }