2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.apex.client.monitoring.rest;
23 import com.google.gson.Gson;
24 import com.google.gson.JsonArray;
25 import com.google.gson.JsonObject;
27 import java.io.PrintWriter;
28 import java.io.StringWriter;
29 import java.util.HashMap;
30 import java.util.LinkedList;
31 import java.util.List;
34 import javax.ws.rs.Consumes;
35 import javax.ws.rs.GET;
36 import javax.ws.rs.Path;
37 import javax.ws.rs.Produces;
38 import javax.ws.rs.QueryParam;
39 import javax.ws.rs.core.MediaType;
40 import javax.ws.rs.core.Response;
42 import org.onap.policy.apex.core.deployment.ApexDeploymentException;
43 import org.onap.policy.apex.core.deployment.EngineServiceFacade;
44 import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
45 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
46 import org.onap.policy.apex.model.enginemodel.concepts.AxEngineModel;
47 import org.slf4j.ext.XLogger;
48 import org.slf4j.ext.XLoggerFactory;
51 * The class represents the root resource exposed at the base URL<br>
52 * The url to access this resource would be in the form {@code <baseURL>/rest/....} <br>
53 * For example: a GET request to the following URL
54 * {@code http://localhost:18989/apexservices/rest/?hostName=localhost&port=12345}
56 * <b>Note:</b> An allocated {@code hostName} and {@code port} query parameter must be included in all requests.
57 * Datasets for different {@code hostName} are completely isolated from one another.
61 @Produces({ MediaType.APPLICATION_JSON })
62 @Consumes({ MediaType.APPLICATION_JSON })
64 public class ApexMonitoringRestResource {
65 // Get a reference to the logger
66 private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexMonitoringRestResource.class);
68 // Set the maximum number of stored data entries to be stored for each engine
69 private static final int maxCachedEntries = 50;
71 // Set up a map separated by host and engine for the data
72 private static final HashMap<String, HashMap<String, List<Counter>>> cache =
73 new HashMap<String, HashMap<String, List<Counter>>>();
75 // Set up a map separated by host for storing the state of periodic events
76 private static final HashMap<String, Boolean> periodicEventsStateCache = new HashMap<String, Boolean>();
79 * Constructor, a new resource director is created for each request.
81 public ApexMonitoringRestResource() {}
84 * Query the engine service for data.
86 * @param hostName the host name of the engine service to connect to.
87 * @param port the port number of the engine service to connect to.
88 * @return a Response object containing the engines service, status and context data in JSON
91 public Response createSession(@QueryParam("hostName") final String hostName, @QueryParam("port") final int port) {
92 final Gson gson = new Gson();
93 final String host = hostName + ":" + port;
94 final EngineServiceFacade engineServiceFacade = new EngineServiceFacade(hostName, port);
97 engineServiceFacade.init();
98 } catch (final ApexDeploymentException e) {
99 final String errorMessage = "Error connecting to Apex Engine Service at " + host;
100 LOGGER.warn(errorMessage + "<br>", e);
101 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errorMessage + "\n" + e.getMessage())
105 final JsonObject responseObject = new JsonObject();
107 // Engine Service data
108 responseObject.addProperty("engine_id", engineServiceFacade.getKey().getId());
109 responseObject.addProperty("model_id",
110 engineServiceFacade.getApexModelKey() != null ? engineServiceFacade.getApexModelKey().getId()
112 responseObject.addProperty("server", hostName);
113 responseObject.addProperty("port", Integer.toString(port));
114 responseObject.addProperty("periodic_events", getPeriodicEventsState(host));
116 // Engine Status data
117 final JsonArray engineStatusList = new JsonArray();
119 for (final AxArtifactKey engineKey : engineServiceFacade.getEngineKeyArray()) {
121 final JsonObject engineStatusObject = new JsonObject();
122 final AxEngineModel axEngineModel = engineServiceFacade.getEngineStatus(engineKey);
123 engineStatusObject.addProperty("timestamp", axEngineModel.getTimeStampString());
124 engineStatusObject.addProperty("id", engineKey.getId());
125 engineStatusObject.addProperty("status", axEngineModel.getState().toString());
126 engineStatusObject.addProperty("last_message", axEngineModel.getStats().getTimeStampString());
127 engineStatusObject.addProperty("up_time", axEngineModel.getStats().getUpTime() / 1000L);
128 engineStatusObject.addProperty("policy_executions", axEngineModel.getStats().getEventCount());
129 engineStatusObject.addProperty("last_policy_duration",
131 getValuesFromCache(host, engineKey.getId() + "_last_policy_duration",
132 axEngineModel.getTimestamp(), axEngineModel.getStats().getLastExecutionTime()),
134 engineStatusObject.addProperty("average_policy_duration",
135 gson.toJson(getValuesFromCache(host, engineKey.getId() + "_average_policy_duration",
136 axEngineModel.getTimestamp(),
137 (long) axEngineModel.getStats().getAverageExecutionTime()), List.class));
138 engineStatusList.add(engineStatusObject);
139 } catch (final ApexException e) {
140 LOGGER.warn("Error getting status of engine with ID " + engineKey.getId() + "<br>", e);
143 responseObject.add("status", engineStatusList);
145 // Engine context data
146 final JsonArray engineContextList = new JsonArray();
147 for (final AxArtifactKey engineKey : engineServiceFacade.getEngineKeyArray()) {
149 final String engineInfo = engineServiceFacade.getEngineInfo(engineKey);
150 if (engineInfo != null && !engineInfo.trim().isEmpty()) {
151 final JsonObject engineContextObject = new JsonObject();
152 engineContextObject.addProperty("id", engineKey.getId());
153 engineContextObject.addProperty("engine_info", engineInfo);
154 engineContextList.add(engineContextObject);
156 } catch (final ApexException e) {
157 LOGGER.warn("Error getting runtime information of engine with ID " + engineKey.getId() + "<br>", e);
160 responseObject.add("context", engineContextList);
162 return Response.ok(responseObject.toString(), MediaType.APPLICATION_JSON).build();
166 * Start/Stop and Apex engine.
168 * @param hostName the host name of the engine service to connect to.
169 * @param port the port number of the engine service to connect to.
170 * @param engineId the id of the engine to be started/stopped.
171 * @param startStop the parameter to start/stop the engine. Expects either "Start" or "Stop"
172 * @return a Response object of type 200
176 public Response startStop(@QueryParam("hostName") final String hostName, @QueryParam("port") final int port,
177 @QueryParam("engineId") final String engineId, @QueryParam("startstop") final String startStop) {
178 final EngineServiceFacade engineServiceFacade = new EngineServiceFacade(hostName, port);
181 engineServiceFacade.init();
182 } catch (final ApexDeploymentException e) {
183 final String errorMessage = "Error connecting to Apex Engine Service at " + hostName + ":" + port;
184 LOGGER.warn(errorMessage + "<br>", e);
185 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errorMessage + "\n" + e.getMessage())
190 final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
191 parameterMap.put("hostname", new String[] { hostName });
192 parameterMap.put("port", new String[] { Integer.toString(port) });
193 parameterMap.put("AxArtifactKey#" + engineId, new String[] { startStop });
194 final AxArtifactKey engineKey = ParameterCheck.getEngineKey(parameterMap);
195 if (startStop.equals("Start")) {
196 engineServiceFacade.startEngine(engineKey);
197 } else if (startStop.equals("Stop")) {
198 engineServiceFacade.stopEngine(engineKey);
200 } catch (final Exception e) {
201 final String errorMessage = "Error calling " + startStop + " on Apex Engine: " + engineId;
202 LOGGER.warn(errorMessage + "<br>", e);
203 final StringWriter sw = new StringWriter();
204 e.printStackTrace(new PrintWriter(sw));
205 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errorMessage + "\n" + sw.toString())
209 return Response.ok("{}").build();
213 * Start/Stop and Apex engine.
215 * @param hostName the host name of the engine service to connect to.
216 * @param port the port number of the engine service to connect to.
217 * @param engineId the id of the engine to be started/stopped.
218 * @param startStop the parameter to start/stop the engine. Expects either "Start" or "Stop"
219 * @param period the time between each event in milliseconds
220 * @return a Response object of type 200
223 @Path("periodiceventstartstop/")
224 public Response periodiceventStartStop(@QueryParam("hostName") final String hostName,
225 @QueryParam("port") final int port, @QueryParam("engineId") final String engineId,
226 @QueryParam("startstop") final String startStop, @QueryParam("period") final long period) {
227 final EngineServiceFacade engineServiceFacade = new EngineServiceFacade(hostName, port);
228 final String host = hostName + ":" + port;
230 engineServiceFacade.init();
231 final Map<String, String[]> parameterMap = new HashMap<String, String[]>();
232 parameterMap.put("hostname", new String[] { hostName });
233 parameterMap.put("port", new String[] { Integer.toString(port) });
234 parameterMap.put("AxArtifactKey#" + engineId, new String[] { startStop });
235 parameterMap.put("period", new String[] { Long.toString(period) });
236 final AxArtifactKey engineKey = ParameterCheck.getEngineKey(parameterMap);
237 if (startStop.equals("Start")) {
238 engineServiceFacade.startPerioidicEvents(engineKey, period);
239 setPeriodicEventsState(host, true);
240 } else if (startStop.equals("Stop")) {
241 engineServiceFacade.stopPerioidicEvents(engineKey);
242 setPeriodicEventsState(host, false);
244 } catch (final ApexDeploymentException e) {
245 final String errorMessage = "Error connecting to Apex Engine Service at " + host;
246 LOGGER.warn(errorMessage + "<br>", e);
247 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errorMessage + "\n" + e.getMessage())
251 return Response.ok("{}").build();
255 * Check if periodic events are running.
257 * @param host the engine's host url
258 * @return a boolean stating if periodic events are running for a given host
260 private Boolean getPeriodicEventsState(final String host) {
261 return periodicEventsStateCache.containsKey(host) ? periodicEventsStateCache.get(host) : false;
265 * Sets the state of periodic events for a host.
267 * @param host the engine's host url
268 * @param boolean that states if periodic events have been started or stopped
270 private void setPeriodicEventsState(final String host, final Boolean isRunning) {
271 periodicEventsStateCache.put(host, isRunning);
275 * This method takes in the latest data entry for an engine, adds it to an existing data set and returns the full
276 * map for that host and engine.
278 * @param host the engine's host url
279 * @param id the engines id
280 * @param timestamp the timestamp of the latest data entry
281 * @param latestValue the value of the latest data entry
282 * @return a list of {@code Counter} objects for that engine
284 private List<Counter> getValuesFromCache(final String host, final String id, final long timestamp,
285 final long latestValue) {
286 SlidingWindowList<Counter> valueList;
288 if (!cache.containsKey(host)) {
289 cache.put(host, new HashMap<String, List<Counter>>());
292 if (cache.get(host).containsKey(id)) {
293 valueList = (SlidingWindowList<Counter>) cache.get(host).get(id);
295 valueList = new SlidingWindowList<Counter>(maxCachedEntries);
297 valueList.add(new Counter(timestamp, latestValue));
299 cache.get(host).put(id, valueList);
305 * A list of values that uses a FIFO sliding window of a fixed size.
307 public class SlidingWindowList<V> extends LinkedList<V> {
308 private static final long serialVersionUID = -7187277916025957447L;
310 private final int maxEntries;
312 public SlidingWindowList(final int maxEntries) {
313 this.maxEntries = maxEntries;
317 public boolean add(final V elm) {
318 if (this.size() > (maxEntries - 1)) {
321 return super.add(elm);
327 * A class used to storing a single data entry for an engine.
329 public class Counter {
330 private long timestamp;
333 public Counter(final long timestamp, final long value) {
334 this.timestamp = timestamp;
338 public long getTimestamp() {
342 public long getValue() {