5e585b4141b2f642e45e13db45a6e9b3018085af
[policy/models.git] / models-sim / policy-models-simulators / src / main / java / org / onap / policy / models / simulators / Main.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * Copyright (C) 2020-2021 AT&T Intellectual Property. All rights reserved.
4  * Modifications Copyright (C) 2020-2021 Bell Canada. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.models.simulators;
23
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.lang.reflect.InvocationTargetException;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Properties;
31 import java.util.concurrent.atomic.AtomicReference;
32 import lombok.AccessLevel;
33 import lombok.Getter;
34 import org.apache.commons.lang3.StringUtils;
35 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
36 import org.onap.policy.common.endpoints.event.comm.TopicSink;
37 import org.onap.policy.common.endpoints.event.comm.TopicSource;
38 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
39 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
40 import org.onap.policy.common.endpoints.parameters.TopicParameters;
41 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
42 import org.onap.policy.common.gson.GsonMessageBodyHandler;
43 import org.onap.policy.common.parameters.BeanValidationResult;
44 import org.onap.policy.common.utils.coder.Coder;
45 import org.onap.policy.common.utils.coder.CoderException;
46 import org.onap.policy.common.utils.coder.StandardCoder;
47 import org.onap.policy.common.utils.network.NetworkUtil;
48 import org.onap.policy.common.utils.resources.ResourceUtils;
49 import org.onap.policy.common.utils.services.Registry;
50 import org.onap.policy.common.utils.services.ServiceManagerContainer;
51 import org.onap.policy.models.sim.dmaap.parameters.DmaapSimParameterGroup;
52 import org.onap.policy.models.sim.dmaap.provider.DmaapSimProvider;
53 import org.onap.policy.models.sim.dmaap.rest.CambriaMessageBodyHandler;
54 import org.onap.policy.models.sim.dmaap.rest.TextMessageBodyHandler;
55 import org.onap.policy.simulators.CdsSimulator;
56 import org.onap.policy.simulators.TopicServer;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 /**
61  * This class runs all simulators specified in the parameter file.
62  */
63 public class Main extends ServiceManagerContainer {
64     private static final Logger logger = LoggerFactory.getLogger(Main.class);
65
66     private static final String CANNOT_CONNECT = "cannot connect to port ";
67
68     @Getter(AccessLevel.PROTECTED)
69     private static Main instance;
70
71
72     /**
73      * Runs the simulators.
74      *
75      * @param paramFile parameter file name
76      */
77     public Main(String paramFile) {
78         super(Main.class.getPackage().getName());
79
80         SimulatorParameters params = readParameters(paramFile);
81         BeanValidationResult result = params.validate("simulators");
82         if (!result.isValid()) {
83             logger.error("invalid parameters:\n{}", result.getResult());
84             throw new IllegalArgumentException("invalid simulator parameters");
85         }
86
87         DmaapSimParameterGroup dmaapProv = params.getDmaapProvider();
88         String dmaapName = (dmaapProv != null ? dmaapProv.getName() : null);
89
90         // dmaap provider
91         if (dmaapProv != null) {
92             String provName = dmaapName.replace("simulator", "provider");
93             AtomicReference<DmaapSimProvider> provRef = new AtomicReference<>();
94             addAction(provName, () -> provRef.set(buildDmaapProvider(dmaapProv)), () -> provRef.get().shutdown());
95         }
96
97         CdsServerParameters cdsServer = params.getGrpcServer();
98
99         // Cds Simulator
100         if (cdsServer != null) {
101             AtomicReference<CdsSimulator> cdsSim = new AtomicReference<>();
102             addAction(cdsServer.getName(), () -> cdsSim.set(buildCdsSimulator(cdsServer)), () -> cdsSim.get().stop());
103         }
104
105         // REST server simulators
106         // @formatter:off
107         for (ClassRestServerParameters restsim : params.getRestServers()) {
108             AtomicReference<HttpServletServer> ref = new AtomicReference<>();
109             if (StringUtils.isNotBlank(restsim.getResourceLocation())) {
110                 String resourceLocationId = restsim.getProviderClass() + "_RESOURCE_LOCATION";
111                 addAction(resourceLocationId,
112                     () -> Registry.register(resourceLocationId, restsim.getResourceLocation()),
113                     () -> Registry.unregister(resourceLocationId));
114             }
115             addAction(restsim.getName(),
116                 () -> ref.set(buildRestServer(dmaapName, restsim)),
117                 () -> ref.get().shutdown());
118         }
119
120         // NOTE: topics must be started AFTER the (dmaap) rest servers
121
122         // topic sinks
123         Map<String, TopicSink> sinks = new HashMap<>();
124         for (TopicParameters topicParams : params.getTopicSinks()) {
125             String topic = topicParams.getTopic();
126             addAction("Sink " + topic,
127                 () -> sinks.put(topic, startSink(topicParams)),
128                 () -> sinks.get(topic).shutdown());
129         }
130
131         // topic sources
132         Map<String, TopicSource> sources = new HashMap<>();
133         for (TopicParameters topicParams : params.getTopicSources()) {
134             String topic = topicParams.getTopic();
135             addAction("Source " + topic,
136                 () -> sources.put(topic, startSource(topicParams)),
137                 () -> sources.get(topic).shutdown());
138         }
139
140         // topic server simulators
141         for (TopicServerParameters topicsim : params.getTopicServers()) {
142             AtomicReference<TopicServer<?>> ref = new AtomicReference<>();
143             addAction(topicsim.getName(),
144                 () -> ref.set(buildTopicServer(topicsim, sinks, sources)),
145                 () -> ref.get().shutdown());
146         }
147         // @formatter:on
148     }
149
150     /**
151      * The main method. The arguments are validated, thus adding the NOSONAR.
152      *
153      * @param args the arguments, the first of which is the name of the parameter file
154      */
155     public static void main(final String[] args) { // NOSONAR
156         /*
157          * Only one argument is used and is validated implicitly by the constructor (i.e.,
158          * file-not-found), thus sonar is disabled.
159          */
160
161         try {
162             if (args.length != 1) {
163                 throw new IllegalArgumentException("arg(s): parameter-file-name");
164             }
165
166             instance = new Main(args[0]);
167             instance.start();
168
169         } catch (RuntimeException e) {
170             logger.error("failed to start simulators", e);
171         }
172     }
173
174     private SimulatorParameters readParameters(String paramFile) {
175         try {
176             var paramsJson = getResourceAsString(paramFile);
177             if (paramsJson == null) {
178                 throw new IllegalArgumentException(new FileNotFoundException(paramFile));
179             }
180
181             String hostName = NetworkUtil.getHostname();
182             logger.info("replacing 'HOST_NAME' with {} in {}", hostName, paramFile);
183
184             paramsJson = paramsJson.replace("${HOST_NAME}", hostName);
185
186             return makeCoder().decode(paramsJson, SimulatorParameters.class);
187
188         } catch (CoderException e) {
189             throw new IllegalArgumentException("cannot decode " + paramFile, e);
190         }
191     }
192
193     private DmaapSimProvider buildDmaapProvider(DmaapSimParameterGroup params) {
194         var prov = new DmaapSimProvider(params);
195         DmaapSimProvider.setInstance(prov);
196         prov.start();
197         return prov;
198     }
199
200     private CdsSimulator buildCdsSimulator(CdsServerParameters params) throws IOException {
201         var cdsSimulator = new CdsSimulator(params.getHost(), params.getPort(), params.getResourceLocation(),
202             params.getSuccessRepeatCount(), params.getRequestedResponseDelayMs());
203         cdsSimulator.start();
204         return cdsSimulator;
205     }
206
207
208     private TopicSink startSink(TopicParameters params) {
209         TopicSink sink = TopicEndpointManager.getManager().addTopicSinks(List.of(params)).get(0);
210         sink.start();
211         return sink;
212     }
213
214     private TopicSource startSource(TopicParameters params) {
215         TopicSource source = TopicEndpointManager.getManager().addTopicSources(List.of(params)).get(0);
216         source.start();
217         return source;
218     }
219
220     private HttpServletServer buildRestServer(String dmaapName, ClassRestServerParameters params) {
221         try {
222             var props = getServerProperties(dmaapName, params);
223             HttpServletServer testServer = makeServer(props);
224             testServer.waitedStart(5000);
225
226             String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + params.getName();
227             String hostName = props.getProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX);
228
229             if (!isTcpPortOpen(hostName, testServer.getPort())) {
230                 throw new IllegalStateException(CANNOT_CONNECT + testServer.getPort());
231             }
232
233             return testServer;
234
235         } catch (InterruptedException e) {
236             Thread.currentThread().interrupt();
237             throw new IllegalStateException("interrupted while building " + params.getName(), e);
238         }
239     }
240
241     private TopicServer<?> buildTopicServer(TopicServerParameters params, Map<String, TopicSink> sinks,
242                     Map<String, TopicSource> sources) {
243         try {
244             // find the desired sink
245             TopicSink sink = sinks.get(params.getSink());
246             if (sink == null) {
247                 throw new IllegalArgumentException("invalid sink topic " + params.getSink());
248             }
249
250             // find the desired source
251             TopicSource source = sources.get(params.getSource());
252             if (source == null) {
253                 throw new IllegalArgumentException("invalid source topic " + params.getSource());
254             }
255
256             // create the topic server
257             return (TopicServer<?>) Class.forName(params.getProviderClass())
258                             .getDeclaredConstructor(TopicSink.class, TopicSource.class).newInstance(sink, source);
259
260         } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException
261                         | SecurityException | ClassNotFoundException e) {
262             throw new IllegalArgumentException("cannot create TopicServer: " + params.getName(), e);
263         }
264     }
265
266     /**
267      * Creates a set of properties, suitable for building a REST server, from the
268      * parameters.
269      *
270      * @param params parameters from which to build the properties
271      * @return a set of properties representing the given parameters
272      */
273     private static Properties getServerProperties(String dmaapName, ClassRestServerParameters params) {
274         final var props = new Properties();
275         props.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, params.getName());
276
277         final String svcpfx = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + params.getName();
278
279         props.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, params.getName());
280         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, params.getHost());
281         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX,
282                         Integer.toString(params.getPort()));
283         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX,
284                         Boolean.toString(params.isHttps()));
285         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX,
286                         params.getProviderClass());
287         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "false");
288         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "false");
289         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "true");
290
291         if (dmaapName != null && dmaapName.equals(params.getName())) {
292             props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER,
293                             String.join(",", CambriaMessageBodyHandler.class.getName(),
294                                             GsonMessageBodyHandler.class.getName(),
295                                             TextMessageBodyHandler.class.getName()));
296         } else {
297             props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER, String.join(",",
298                             GsonMessageBodyHandler.class.getName(), TextMessageBodyHandler.class.getName()));
299         }
300
301         return props;
302     }
303
304     // the following methods may be overridden by junit tests
305
306     protected String getResourceAsString(String resourceName) {
307         return ResourceUtils.getResourceAsString(resourceName);
308     }
309
310     protected Coder makeCoder() {
311         return new StandardCoder();
312     }
313
314     protected HttpServletServer makeServer(Properties props) {
315         return HttpServletServerFactoryInstance.getServerFactory().build(props).get(0);
316     }
317
318     protected boolean isTcpPortOpen(String hostName, int port) throws InterruptedException {
319         return NetworkUtil.isTcpPortOpen(hostName, port, 100, 200L);
320     }
321 }