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