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