a0b709b51d188f97fa2ce1542800d39009e2763a
[policy/drools-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * feature-state-management
4  * ================================================================================
5  * Copyright (C) 2017-2019 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.drools.statemanagement;
22
23 import java.io.IOException;
24 import java.util.List;
25 import java.util.Properties;
26 import org.onap.policy.common.capabilities.Startable;
27 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
28 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
29 import org.onap.policy.common.im.IntegrityMonitor;
30 import org.onap.policy.common.im.IntegrityMonitorException;
31 import org.onap.policy.drools.utils.PropertyUtil;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * This class extends 'IntegrityMonitor' for use in the 'Drools PDP' virtual machine. The included
37  * audits are 'Database' and 'Repository'.
38  */
39 public class DroolsPDPIntegrityMonitor extends IntegrityMonitor {
40
41     private static final String INVALID_PROPERTY_VALUE = "init: property {} does not have the expected value of {}";
42
43     // get an instance of logger
44     private static final Logger logger = LoggerFactory.getLogger(DroolsPDPIntegrityMonitor.class);
45
46     // static global instance
47     private static DroolsPDPIntegrityMonitor im = null;
48
49     // list of audits to run
50     private static AuditBase[] audits = new AuditBase[] {DbAudit.getInstance(), RepositoryAudit.getInstance()};
51
52     private static Properties subsystemTestProperties = null;
53
54     private static final String PROPERTIES_NAME = "feature-state-management.properties";
55
56     /**
57      * Constructor - pass arguments to superclass, but remember properties.
58      *
59      * @param resourceName unique name of this Integrity Monitor
60      * @param url the JMX URL of the MBean server
61      * @param properties properties used locally, as well as by 'IntegrityMonitor'
62      * @throws IntegrityMonitorException (passed from superclass)
63      */
64     private DroolsPDPIntegrityMonitor(String resourceName, Properties consolidatedProperties)
65             throws IntegrityMonitorException {
66         super(resourceName, consolidatedProperties);
67     }
68
69     private static void missingProperty(String prop) throws IntegrityMonitorException {
70         String msg = "init: missing IntegrityMonitor property: ".concat(prop);
71         logger.error(msg);
72         throw new IntegrityMonitorException(msg);
73     }
74
75     private static void logPropertyValue(String prop, String val) {
76         if (logger.isInfoEnabled()) {
77             String msg = "\n\n    init: property: " + prop + " = " + val + "\n";
78             logger.info(msg);
79         }
80     }
81
82     /**
83      * Static initialization -- create Drools Integrity Monitor, and an HTTP server to handle REST
84      * 'test' requests.
85      *
86      * @throws IntegrityMonitorException exception
87      */
88     public static DroolsPDPIntegrityMonitor init(String configDir) throws IntegrityMonitorException {
89
90         logger.info("init: Entering and invoking PropertyUtil.getProperties() on '{}'", configDir);
91
92         // read in properties
93         Properties stateManagementProperties = getProperties(configDir);
94
95         // fetch and verify definitions of some properties, adding defaults where
96         // appropriate
97         // (the 'IntegrityMonitor' constructor does some additional verification)
98
99         checkPropError(stateManagementProperties, StateManagementProperties.TEST_HOST);
100         checkPropError(stateManagementProperties, StateManagementProperties.TEST_PORT);
101
102         addDefaultPropError(stateManagementProperties, StateManagementProperties.TEST_SERVICES,
103                 StateManagementProperties.TEST_SERVICES_DEFAULT);
104
105         addDefaultPropError(stateManagementProperties, StateManagementProperties.TEST_REST_CLASSES,
106                 StateManagementProperties.TEST_REST_CLASSES_DEFAULT);
107
108         addDefaultPropWarn(stateManagementProperties, StateManagementProperties.TEST_MANAGED,
109                 StateManagementProperties.TEST_MANAGED_DEFAULT);
110
111         addDefaultPropWarn(stateManagementProperties, StateManagementProperties.TEST_SWAGGER,
112                 StateManagementProperties.TEST_SWAGGER_DEFAULT);
113
114         checkPropError(stateManagementProperties, StateManagementProperties.RESOURCE_NAME);
115         checkPropError(stateManagementProperties, StateManagementProperties.FP_MONITOR_INTERVAL);
116         checkPropError(stateManagementProperties, StateManagementProperties.FAILED_COUNTER_THRESHOLD);
117         checkPropError(stateManagementProperties, StateManagementProperties.TEST_TRANS_INTERVAL);
118         checkPropError(stateManagementProperties, StateManagementProperties.WRITE_FPC_INTERVAL);
119         checkPropError(stateManagementProperties, StateManagementProperties.SITE_NAME);
120         checkPropError(stateManagementProperties, StateManagementProperties.NODE_TYPE);
121         checkPropError(stateManagementProperties, StateManagementProperties.DEPENDENCY_GROUPS);
122         checkPropError(stateManagementProperties, StateManagementProperties.DB_DRIVER);
123         checkPropError(stateManagementProperties, StateManagementProperties.DB_URL);
124         checkPropError(stateManagementProperties, StateManagementProperties.DB_USER);
125         checkPropError(stateManagementProperties, StateManagementProperties.DB_PWD);
126
127         final String testHost = stateManagementProperties.getProperty(StateManagementProperties.TEST_HOST);
128         final String testPort = stateManagementProperties.getProperty(StateManagementProperties.TEST_PORT);
129         final String resourceName = stateManagementProperties.getProperty(StateManagementProperties.RESOURCE_NAME);
130
131         subsystemTestProperties = stateManagementProperties;
132
133         // Now that we've validated the properties, create Drools Integrity Monitor
134         // with these properties.
135         im = makeMonitor(resourceName, stateManagementProperties);
136         logger.info("init: New DroolsPDPIntegrityMonitor instantiated, resourceName = {}", resourceName);
137
138         // create http server
139         makeRestServer(testHost, testPort, stateManagementProperties);
140         logger.info("init: Exiting and returning DroolsPDPIntegrityMonitor");
141
142         return im;
143     }
144
145     /**
146      * Makes an Integrity Monitor.
147      *
148      * @param resourceName unique name of this Integrity Monitor
149      * @param properties properties used to configure the Integrity Monitor
150      * @return monitor object
151      * @throws IntegrityMonitorException exception
152      */
153     private static DroolsPDPIntegrityMonitor makeMonitor(String resourceName, Properties properties)
154             throws IntegrityMonitorException {
155
156         try {
157             return new DroolsPDPIntegrityMonitor(resourceName, properties);
158
159         } catch (Exception e) {
160             throw new IntegrityMonitorException(e);
161         }
162     }
163
164     /**
165      * Makes a rest server for the Integrity Monitor.
166      *
167      * @param testHost host name
168      * @param testPort port
169      * @param properties properties used to configure the rest server
170      * @throws IntegrityMonitorException exception
171      */
172     private static void makeRestServer(String testHost, String testPort, Properties properties)
173             throws IntegrityMonitorException {
174
175         try {
176             logger.info("init: Starting HTTP server, addr= {}:{}", testHost, testPort);
177
178             IntegrityMonitorRestServer server = new IntegrityMonitorRestServer();
179             server.init(properties);
180
181         } catch (Exception e) {
182             logger.error("init: Caught Exception attempting to start server on testPort= {} message:", testPort, e);
183             throw new IntegrityMonitorException(e);
184         }
185     }
186
187     /**
188      * Gets the properties from the property file.
189      *
190      * @param configDir directory containing the property file
191      * @return the properties
192      * @throws IntegrityMonitorException exception
193      */
194     private static Properties getProperties(String configDir) throws IntegrityMonitorException {
195         try {
196             return PropertyUtil.getProperties(configDir + "/" + PROPERTIES_NAME);
197
198         } catch (IOException e) {
199             throw new IntegrityMonitorException(e);
200         }
201     }
202
203     /**
204      * Checks that a property is defined.
205      *
206      * @param props set of properties
207      * @param name name of the property to check
208      * @throws IntegrityMonitorException exception
209      */
210     private static void checkPropError(Properties props, String name) throws IntegrityMonitorException {
211         String val = props.getProperty(name);
212         if (val == null) {
213             missingProperty(name);
214         }
215
216         logPropertyValue(name, val);
217     }
218
219     /**
220      * Checks a property's value to verify that it matches the expected value. If the property is
221      * not defined, then it is added to the property set, with the expected value. Logs an error if
222      * the property is defined, but does not have the expected value.
223      *
224      * @param props set of properties
225      * @param name name of the property to check
226      * @param expected expected/default value
227      */
228     private static void addDefaultPropError(Properties props, String name, String expected) {
229         String val = props.getProperty(name);
230         if (val == null) {
231             props.setProperty(name, expected);
232
233         } else if (!val.equals(expected)) {
234             logger.error(INVALID_PROPERTY_VALUE, name, expected);
235         }
236
237         logPropertyValue(name, val);
238     }
239
240     /**
241      * Checks a property's value to verify that it matches the expected value. If the property is
242      * not defined, then it is added to the property set, with the expected value. Logs a warning if
243      * the property is defined, but does not have the expected value.
244      *
245      * @param props set of properties
246      * @param name name of the property to check
247      * @param expected expected/default value
248      */
249     private static void addDefaultPropWarn(Properties props, String name, String dflt) {
250         String val = props.getProperty(name);
251         if (val == null) {
252             props.setProperty(name, dflt);
253
254         } else if (!val.equals(dflt)) {
255             logger.warn(INVALID_PROPERTY_VALUE, name, dflt);
256         }
257
258         logPropertyValue(name, val);
259     }
260
261     /**
262      * Run tests (audits) unique to Drools PDP VM (Database + Repository).
263      */
264     @Override
265     public void subsystemTest() throws IntegrityMonitorException {
266         logger.info("DroolsPDPIntegrityMonitor.subsystemTest called");
267
268         // clear all responses (non-null values indicate an error)
269         for (AuditBase audit : audits) {
270             audit.setResponse(null);
271         }
272
273         // invoke all of the audits
274         for (AuditBase audit : audits) {
275             try {
276                 // invoke the audit (responses are stored within the audit object)
277                 audit.invoke(subsystemTestProperties);
278             } catch (Exception e) {
279                 logger.error("{} audit error", audit.getName(), e);
280                 if (audit.getResponse() == null) {
281                     // if there is no current response, use the exception message
282                     audit.setResponse(e.getMessage());
283                 }
284             }
285         }
286
287         // will contain list of subsystems where the audit failed
288         String responseMsg = "";
289
290         // Loop through all of the audits, and see which ones have failed.
291         // NOTE: response information is stored within the audit objects
292         // themselves -- only one can run at a time.
293         for (AuditBase audit : audits) {
294             String response = audit.getResponse();
295             if (response != null) {
296                 // the audit has failed -- add subsystem and
297                 // and 'responseValue' with the new information
298                 responseMsg = responseMsg.concat("\n" + audit.getName() + ": " + response);
299             }
300         }
301
302         if (!responseMsg.isEmpty()) {
303             throw new IntegrityMonitorException(responseMsg);
304         }
305     }
306
307     /* ============================================================ */
308
309     /**
310      * This is the base class for audits invoked in 'subsystemTest'.
311      */
312     public abstract static class AuditBase {
313         // name of the audit
314         protected String name;
315
316         // non-null indicates the error response
317         protected String response;
318
319         /**
320          * Constructor - initialize the name, and clear the initial response.
321          *
322          * @param name name of the audit
323          */
324         public AuditBase(String name) {
325             this.name = name;
326             this.response = null;
327         }
328
329         /**
330          * Get the name.
331          *
332          * @return the name of this audit
333          */
334         public String getName() {
335             return name;
336         }
337
338         /**
339          * Get the response.
340          *
341          * @return the response String (non-null indicates the error message)
342          */
343         public String getResponse() {
344             return response;
345         }
346
347         /**
348          * Set the response string to the specified value.
349          *
350          * @param value the new value of the response string (null = no errors)
351          */
352         public void setResponse(String value) {
353             response = value;
354         }
355
356         /**
357          * Abstract method to invoke the audit.
358          *
359          * @param persistenceProperties Used for DB access
360          * @throws Exception passed in by the audit
361          */
362         abstract void invoke(Properties persistenceProperties) throws Exception;
363     }
364
365     public static class IntegrityMonitorRestServer implements Startable {
366         protected volatile HttpServletServer server = null;
367         protected volatile Properties integrityMonitorRestServerProperties = null;
368
369         public void init(Properties props) {
370             this.integrityMonitorRestServerProperties = props;
371             this.start();
372         }
373
374         @Override
375         public boolean start() {
376             try {
377                 List<HttpServletServer> servers = HttpServletServerFactoryInstance.getServerFactory()
378                                 .build(integrityMonitorRestServerProperties);
379
380                 if (!servers.isEmpty()) {
381                     server = servers.get(0);
382
383                     waitServerStart();
384                 }
385             } catch (Exception e) {
386                 logger.error("Exception building servers", e);
387                 return false;
388             }
389
390             return true;
391         }
392
393         private void waitServerStart() {
394             try {
395                 server.waitedStart(5);
396             } catch (Exception e) {
397                 logger.error("Exception waiting for servers to start: ", e);
398             }
399         }
400
401         @Override
402         public boolean stop() {
403             try {
404                 server.stop();
405             } catch (Exception e) {
406                 logger.error("Exception during stop", e);
407             }
408
409             return true;
410         }
411
412         @Override
413         public void shutdown() {
414             this.stop();
415         }
416
417         @Override
418         public synchronized boolean isAlive() {
419             return this.integrityMonitorRestServerProperties != null;
420         }
421     }
422
423     /**
424      * Returns the instance.
425      *
426      * @return DroolsPDPIntegrityMonitor object
427      * @throws IntegrityMonitorException exception
428      */
429     public static DroolsPDPIntegrityMonitor getInstance() throws IntegrityMonitorException {
430         if (logger.isDebugEnabled()) {
431             logger.debug("getInstance() called");
432         }
433         if (im == null) {
434             String msg = "No DroolsPDPIntegrityMonitor instance exists."
435                     + " Please use the method DroolsPDPIntegrityMonitor init(String configDir)";
436             throw new IntegrityMonitorException(msg);
437         } else {
438             return im;
439         }
440     }
441 }