Clean up and enhancement of Actor re-design
[policy/models.git] / models-interactions / model-actors / actorServiceProvider / src / main / java / org / onap / policy / controlloop / actorserviceprovider / impl / ActorImpl.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.impl;
22
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.function.Function;
30 import org.onap.policy.common.parameters.BeanValidationResult;
31 import org.onap.policy.controlloop.actorserviceprovider.Operator;
32 import org.onap.policy.controlloop.actorserviceprovider.Util;
33 import org.onap.policy.controlloop.actorserviceprovider.parameters.ParameterValidationRuntimeException;
34 import org.onap.policy.controlloop.actorserviceprovider.spi.Actor;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Implementation of an actor.
40  */
41 public class ActorImpl extends StartConfigPartial<Map<String, Object>> implements Actor {
42     private static final Logger logger = LoggerFactory.getLogger(ActorImpl.class);
43
44     /**
45      * Maps a name to an operator.
46      */
47     private final Map<String, Operator> name2operator = new ConcurrentHashMap<>();
48
49     /**
50      * Constructs the object.
51      *
52      * @param name actor name
53      */
54     public ActorImpl(String name) {
55         super(name);
56     }
57
58     /**
59      * Adds an operator supported by this actor.
60      *
61      * @param operator operation to be added
62      */
63     protected synchronized void addOperator(Operator operator) {
64         /*
65          * This method is "synchronized" to prevent the state from changing while the
66          * operator is added. The map, itself, does not need synchronization as it's a
67          * concurrent map.
68          */
69
70         if (isConfigured()) {
71             throw new IllegalStateException("attempt to set operators on a configured actor: " + getName());
72         }
73
74         name2operator.compute(operator.getName(), (opName, existingOp) -> {
75             if (existingOp == null) {
76                 return operator;
77             }
78
79             logger.warn("duplicate names for actor operation {}.{}: {}, ignoring {}", getName(), opName,
80                             existingOp.getClass().getSimpleName(), operator.getClass().getSimpleName());
81             return existingOp;
82         });
83     }
84
85     @Override
86     public String getName() {
87         return getFullName();
88     }
89
90     @Override
91     public Operator getOperator(String name) {
92         Operator operator = name2operator.get(name);
93         if (operator == null) {
94             throw new IllegalArgumentException("unknown operation " + getName() + "." + name);
95         }
96
97         return operator;
98     }
99
100     @Override
101     public Collection<Operator> getOperators() {
102         return name2operator.values();
103     }
104
105     @Override
106     public Set<String> getOperationNames() {
107         return name2operator.keySet();
108     }
109
110     /**
111      * For each operation, it looks for a set of parameters by the same name and, if
112      * found, configures the operation with the parameters.
113      */
114     @Override
115     protected void doConfigure(Map<String, Object> parameters) {
116         final String actorName = getName();
117         logger.info("configuring operations for actor {}", actorName);
118
119         BeanValidationResult valres = new BeanValidationResult(actorName, parameters);
120
121         // function that creates operator-specific parameters, given the operation name
122         Function<String, Map<String, Object>> opParamsMaker = makeOperatorParameters(parameters);
123
124         for (Operator operator : name2operator.values()) {
125             String operName = operator.getName();
126             Map<String, Object> subparams = opParamsMaker.apply(operName);
127
128             if (subparams != null) {
129
130                 try {
131                     operator.configure(subparams);
132
133                 } catch (ParameterValidationRuntimeException e) {
134                     logger.warn("failed to configure operation {}.{}", actorName, operName, e);
135                     valres.addResult(e.getResult());
136
137                 } catch (RuntimeException e) {
138                     logger.warn("failed to configure operation {}.{}", actorName, operName, e);
139                 }
140
141             } else if (operator.isConfigured()) {
142                 logger.warn("missing configuration parameters for operation {}.{}; using previous parameters",
143                                 actorName, operName);
144
145             } else {
146                 logger.warn("missing configuration parameters for operation {}.{}; operation cannot be started",
147                                 actorName, operName);
148             }
149         }
150     }
151
152     /**
153      * Extracts the operator parameters from the actor parameters, for a given operator.
154      * This method assumes each operation has its own set of parameters.
155      *
156      * @param actorParameters actor parameters
157      * @return a function to extract the operator parameters from the actor parameters.
158      *         Note: this function may return {@code null} if there are no parameters for
159      *         the given operation name
160      */
161     protected Function<String, Map<String, Object>> makeOperatorParameters(Map<String, Object> actorParameters) {
162
163         return operName -> Util.translateToMap(getName() + "." + operName, actorParameters.get(operName));
164     }
165
166     /**
167      * Starts each operation.
168      */
169     @Override
170     protected void doStart() {
171         final String actorName = getName();
172         logger.info("starting operations for actor {}", actorName);
173
174         for (Operator oper : name2operator.values()) {
175             if (oper.isConfigured()) {
176                 Util.runFunction(oper::start, "failed to start operation {}.{}", actorName, oper.getName());
177
178             } else {
179                 logger.warn("not starting unconfigured operation {}.{}", actorName, oper.getName());
180             }
181         }
182     }
183
184     /**
185      * Stops each operation.
186      */
187     @Override
188     protected void doStop() {
189         final String actorName = getName();
190         logger.info("stopping operations for actor {}", actorName);
191
192         // @formatter:off
193         name2operator.values().forEach(
194             oper -> Util.runFunction(oper::stop, "failed to stop operation {}.{}", actorName, oper.getName()));
195         // @formatter:on
196     }
197
198     /**
199      * Shuts down each operation.
200      */
201     @Override
202     protected void doShutdown() {
203         final String actorName = getName();
204         logger.info("shutting down operations for actor {}", actorName);
205
206         // @formatter:off
207         name2operator.values().forEach(oper -> Util.runFunction(oper::shutdown,
208                         "failed to shutdown operation {}.{}", actorName, oper.getName()));
209         // @formatter:on
210     }
211
212     // TODO old code: remove lines down to **HERE**
213
214     @Override
215     public String actor() {
216         return null;
217     }
218
219     @Override
220     public List<String> recipes() {
221         return Collections.emptyList();
222     }
223
224     @Override
225     public List<String> recipeTargets(String recipe) {
226         return Collections.emptyList();
227     }
228
229     @Override
230     public List<String> recipePayloads(String recipe) {
231         return Collections.emptyList();
232     }
233
234     // **HERE**
235 }