Do not require context in ControlLoopOperationParams
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / main / java / org / onap / policy / controlloop / actorserviceprovider / parameters / ControlLoopOperationParams.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.parameters;
22
23 import java.util.Map;
24 import java.util.UUID;
25 import java.util.concurrent.CompletableFuture;
26 import java.util.concurrent.Executor;
27 import java.util.concurrent.ForkJoinPool;
28 import java.util.function.Consumer;
29 import lombok.AllArgsConstructor;
30 import lombok.Builder;
31 import lombok.EqualsAndHashCode;
32 import lombok.Getter;
33 import org.onap.policy.common.parameters.BeanValidationResult;
34 import org.onap.policy.common.parameters.BeanValidator;
35 import org.onap.policy.common.parameters.annotations.NotNull;
36 import org.onap.policy.controlloop.VirtualControlLoopEvent;
37 import org.onap.policy.controlloop.actorserviceprovider.ActorService;
38 import org.onap.policy.controlloop.actorserviceprovider.Operation;
39 import org.onap.policy.controlloop.actorserviceprovider.OperationOutcome;
40 import org.onap.policy.controlloop.actorserviceprovider.Util;
41 import org.onap.policy.controlloop.actorserviceprovider.controlloop.ControlLoopEventContext;
42 import org.onap.policy.controlloop.policy.Target;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * Parameters for control loop operations. The executor defaults to
48  * {@link ForkJoinPool#commonPool()}, but may be overridden.
49  */
50 @Getter
51 @Builder(toBuilder = true)
52 @AllArgsConstructor
53 @EqualsAndHashCode
54 public class ControlLoopOperationParams {
55     private static final Logger logger = LoggerFactory.getLogger(ControlLoopOperationParams.class);
56
57     /**
58      * Actor name.
59      */
60     @NotNull
61     private String actor;
62
63     /**
64      * Actor service in which to find the actor/operation.
65      */
66     @NotNull
67     private ActorService actorService;
68
69     /**
70      * Event for which the operation applies.
71      */
72     private ControlLoopEventContext context;
73
74     /**
75      * If {@code null}, this value is extracted from the context.
76      */
77     private UUID requestId;
78
79     /**
80      * Executor to use to run the operation.
81      */
82     @NotNull
83     @Builder.Default
84     private Executor executor = ForkJoinPool.commonPool();
85
86     /**
87      * Operation name.
88      */
89     @NotNull
90     private String operation;
91
92     /**
93      * Payload data for the request.
94      */
95     private Map<String, Object> payload;
96
97     /**
98      * {@code True} if the preprocessing steps have already been executed, {@code false}
99      * otherwise.
100      */
101     private boolean preprocessed;
102
103     /**
104      * Number of retries allowed, or {@code null} if no retries.
105      */
106     private Integer retry;
107
108     /**
109      * The entity's target information. May be {@code null}, depending on the requirement
110      * of the operation to be invoked.
111      */
112     private Target target;
113
114     /**
115      * Target entity.
116      */
117     @NotNull
118     private String targetEntity;
119
120     /**
121      * Timeout, in seconds, or {@code null} if no timeout. Zero and negative values also
122      * imply no timeout.
123      */
124     @Builder.Default
125     private Integer timeoutSec = 300;
126
127     /**
128      * The function to invoke when the operation starts. This is optional.
129      * <p/>
130      * Note: this may be invoked multiple times, but with different actor/operations. That
131      * may happen if the current operation requires other operations to be performed first
132      * (e.g., A&AI queries, guard checks).
133      */
134     private Consumer<OperationOutcome> startCallback;
135
136     /**
137      * The function to invoke when the operation completes. This is optional.
138      * <p/>
139      * Note: this may be invoked multiple times, but with different actor/operations. That
140      * may happen if the current operation requires other operations to be performed first
141      * (e.g., A&AI queries, guard checks).
142      */
143     private Consumer<OperationOutcome> completeCallback;
144
145     /**
146      * Starts the specified operation.
147      *
148      * @return a future that will return the result of the operation
149      * @throws IllegalArgumentException if the parameters are invalid
150      */
151     public CompletableFuture<OperationOutcome> start() {
152         return build().start();
153     }
154
155     /**
156      * Builds the specified operation.
157      *
158      * @return a new operation
159      * @throws IllegalArgumentException if the parameters are invalid
160      */
161     public Operation build() {
162         BeanValidationResult result = validate();
163         if (!result.isValid()) {
164             logger.warn("parameter error in operation {}.{} for {}:\n{}", getActor(), getOperation(), getRequestId(),
165                             result.getResult());
166             throw new IllegalArgumentException("invalid parameters");
167         }
168
169         // @formatter:off
170         return actorService
171                     .getActor(getActor())
172                     .getOperator(getOperation())
173                     .buildOperation(this);
174         // @formatter:on
175     }
176
177     /**
178      * Gets the requested ID of the associated event.
179      *
180      * @return the event's request ID, or {@code null} if no request ID is available
181      */
182     public UUID getRequestId() {
183         if (requestId == null && context != null && context.getEvent() != null) {
184             // cache the request ID
185             requestId = context.getEvent().getRequestId();
186         }
187
188         return requestId;
189     }
190
191     /**
192      * Makes an operation outcome, populating it from the parameters.
193      *
194      * @return a new operation outcome
195      */
196     public OperationOutcome makeOutcome() {
197         OperationOutcome outcome = new OperationOutcome();
198         outcome.setActor(getActor());
199         outcome.setOperation(getOperation());
200         outcome.setTarget(targetEntity);
201
202         return outcome;
203     }
204
205     /**
206      * Invokes the callback to indicate that the operation has started. Any exceptions
207      * generated by the callback are logged, but not re-thrown.
208      *
209      * @param operation the operation that is being started
210      */
211     public void callbackStarted(OperationOutcome operation) {
212         logger.info("started operation {}.{} for {}", operation.getActor(), operation.getOperation(), getRequestId());
213
214         if (startCallback != null) {
215             Util.runFunction(() -> startCallback.accept(operation), "{}.{}: start-callback threw an exception for {}",
216                             operation.getActor(), operation.getOperation(), getRequestId());
217         }
218     }
219
220     /**
221      * Invokes the callback to indicate that the operation has completed. Any exceptions
222      * generated by the callback are logged, but not re-thrown.
223      *
224      * @param operation the operation that is being started
225      */
226     public void callbackCompleted(OperationOutcome operation) {
227         logger.info("completed operation {}.{} outcome={} for {}", operation.getActor(), operation.getOperation(),
228                         operation.getResult(), getRequestId());
229
230         if (completeCallback != null) {
231             Util.runFunction(() -> completeCallback.accept(operation),
232                             "{}.{}: complete-callback threw an exception for {}", operation.getActor(),
233                             operation.getOperation(), getRequestId());
234         }
235     }
236
237     /**
238      * Validates the parameters.
239      *
240      * @return the validation result
241      */
242     public BeanValidationResult validate() {
243         BeanValidationResult result =
244                         new BeanValidator().validateTop(ControlLoopOperationParams.class.getSimpleName(), this);
245
246         // validate that we have a request ID, or that we can get it from the context's
247         // event
248
249         if (context == null) {
250             // no context specified - invoker must provide a request ID then
251             result.validateNotNull("requestId", requestId);
252
253         } else if (requestId == null) {
254             // have a context, but no request ID - check the context's event for the
255             // request ID
256             BeanValidationResult contextResult = new BeanValidationResult("context", context);
257             VirtualControlLoopEvent event = context.getEvent();
258             contextResult.validateNotNull("event", event);
259
260             if (event != null) {
261                 // cache the request id for later use
262                 BeanValidationResult eventResult = new BeanValidationResult("event", event);
263                 eventResult.validateNotNull("requestId", event.getRequestId());
264
265                 contextResult.addResult(eventResult);
266             }
267
268             result.addResult(contextResult);
269         }
270
271         return result;
272     }
273 }