409297c92740be6e5cd2b41eb72c866c7422b450
[so.git] / bpmn / MSOCoreBPMN / src / main / java / org / openecomp / mso / bpmn / core / HealthCheckHandler.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * OPENECOMP - MSO
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.openecomp.mso.bpmn.core;
22
23 import org.apache.http.client.config.RequestConfig;
24 import org.apache.http.impl.client.CloseableHttpClient;
25 import org.apache.http.impl.client.HttpClientBuilder;
26 import org.apache.http.HttpResponse;
27 import org.apache.http.HttpStatus;
28 import org.apache.http.HttpEntity;
29 import org.apache.http.client.methods.HttpPost;
30 import org.apache.http.entity.StringEntity;
31
32 import java.io.BufferedReader;
33 import java.io.InputStream;
34 import java.io.InputStreamReader;
35 import java.util.Base64;
36 import java.util.Map;
37 import java.util.Properties;
38 import java.util.UUID;
39
40 import org.openecomp.mso.logger.MsoLogger;
41 import org.openecomp.mso.utils.UUIDChecker;
42 import org.openecomp.mso.HealthCheckUtils;
43 import org.openecomp.mso.logger.MessageEnum;
44 import org.openecomp.mso.utils.CryptoUtils;
45 import javax.ws.rs.GET;
46 import javax.ws.rs.HEAD;
47 import javax.ws.rs.Path;
48 import javax.ws.rs.Produces;
49 import javax.ws.rs.QueryParam;
50 import javax.ws.rs.core.Response;
51
52 import org.camunda.bpm.engine.ProcessEngines;
53
54 @Path("/")
55 public class HealthCheckHandler  {
56
57     private static MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL);
58     private static final String SITENAME = "mso.sitename";
59     private static final String ADPTER_ENDPOINT = "mso.adapters.db.endpoint";
60     private static final String CONFIG = "mso.bpmn.urn.properties";
61     private static final String PENGINE_PROPERTY = "processengine.properties";
62     private static final String PENGINE_PARAM = "processEngineName";
63     private static final String CREDENTIAL = "mso.adapters.db.auth";
64     private static final String MSOKEY = "mso.msoKey";
65
66     private static final String CHECK_HTML = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Health Check</title></head><body>Application ready</body></html>";
67     private static final String NOT_FOUND = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Application Not Started</title></head><body>Application not started. Properties file missing or invalid or database Connection failed</body></html>";
68     private static final String NOT_HEALTHY = "<!DOCTYPE html><html><head><meta charset=\"ISO-8859-1\"><title>Application Not Started</title></head><body>Application not available or at least one of the sub-modules is not available.</body></html>";
69     public static final Response HEALTH_CHECK_RESPONSE = Response.status (HttpStatus.SC_OK)
70             .entity (CHECK_HTML)
71             .build ();
72     public static final Response HEALTH_CHECK_NOK_RESPONSE = Response.status (HttpStatus.SC_SERVICE_UNAVAILABLE)
73             .entity (NOT_HEALTHY)
74             .  build ();
75     public static final Response NOT_STARTED_RESPONSE = Response.status (HttpStatus.SC_SERVICE_UNAVAILABLE)
76             .entity (NOT_FOUND)
77             .build ();
78
79     @HEAD
80     @GET
81     @Path("/nodehealthcheck")
82     @Produces("text/html")
83     public Response nodeHealthcheck () {
84         MsoLogger.setServiceName ("NodeHealthcheck");
85         // Generate a Request Id
86         String requestId = UUIDChecker.generateUUID(msoLogger);
87
88         PropertyConfiguration propertyConfiguration = PropertyConfiguration.getInstance();
89         Map<String,String> props = propertyConfiguration.getProperties(CONFIG);
90
91         if (props == null) {
92
93             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(),  MsoLogger.ErrorCode.AvailabilityError, "Unable to load " + CONFIG);
94
95             return NOT_STARTED_RESPONSE;
96         }
97
98         String siteName = props.get(SITENAME);
99         String endpoint = props.get(ADPTER_ENDPOINT);
100
101         if (null == siteName || siteName.length () == 0 || null == endpoint || endpoint.length () == 0) {
102
103             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, "Unable to load key attributes (" + SITENAME + " or " + ADPTER_ENDPOINT + ") from the config file:" + CONFIG);
104
105             return NOT_STARTED_RESPONSE;
106         }
107
108         try {
109             if (!this.getSiteStatus (endpoint, siteName, props.get(CREDENTIAL), props.get(MSOKEY))) {
110                 msoLogger.debug("This site is currently disabled for maintenance.");
111                 return HEALTH_CHECK_NOK_RESPONSE;
112             }
113         } catch (Exception e) {
114
115             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while getting SiteStatus", e);
116
117             msoLogger.debug("Exception while getting SiteStatus");
118             return NOT_STARTED_RESPONSE;
119         }
120
121
122         HealthCheckUtils healthCheck = new HealthCheckUtils ();
123         if (healthCheck.verifyNodeHealthCheck(HealthCheckUtils.NodeType.BPMN, requestId)) {
124             msoLogger.debug("nodeHealthcheck - Successful");
125             return HealthCheckUtils.HEALTH_CHECK_RESPONSE;
126         } else {
127             msoLogger.debug("nodeHealthcheck - At leaset one of the sub-modules is not available.");
128             return  HealthCheckUtils.HEALTH_CHECK_NOK_RESPONSE;
129         }
130     }
131
132     @HEAD
133     @GET
134     @Path("/healthcheck")
135     @Produces("text/html")
136     public Response healthcheck (@QueryParam("requestId") String requestId) {
137         MsoLogger.setServiceName ("Healthcheck");
138         verifyOldUUID(requestId);
139
140         PropertyConfiguration propertyConfiguration = PropertyConfiguration.getInstance();
141         Map<String,String> props = propertyConfiguration.getProperties(CONFIG);
142
143         if (props == null) {
144
145             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(),  MsoLogger.ErrorCode.AvailabilityError, "Unable to load " + CONFIG);
146
147             return NOT_STARTED_RESPONSE;
148         }
149
150         String siteName = props.get(SITENAME);
151         String endpoint = props.get(ADPTER_ENDPOINT);
152
153         if (null == siteName || siteName.length () == 0 || null == endpoint || endpoint.length () == 0) {
154
155             msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError, "Unable to load key attributes (" + SITENAME + " or " + ADPTER_ENDPOINT + ") from the config file:" + CONFIG);
156
157             return NOT_STARTED_RESPONSE;
158         }
159
160         try {
161             if (!this.getSiteStatus (endpoint, siteName, props.get(CREDENTIAL), props.get(MSOKEY))) {
162                 msoLogger.debug("This site is currently disabled for maintenance.");
163                 return HEALTH_CHECK_NOK_RESPONSE;
164             }
165         } catch (Exception e) {
166
167             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while getting SiteStatus", e);
168
169             msoLogger.debug("Exception while getting SiteStatus");
170             return NOT_STARTED_RESPONSE;
171         }
172
173         try {
174             InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(PENGINE_PROPERTY);
175             Properties prop = new Properties();
176             prop.load(stream);
177             String [] engineNames =  prop.getProperty(PENGINE_PARAM).split(",");
178             for (String engine : engineNames) {
179                 ProcessEngines.getProcessEngine(engine).getIdentityService().createGroupQuery().list();
180             }
181         } catch (final Exception e) {
182
183             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Exception while verifying Camunda engine", e);
184
185             msoLogger.debug("Exception while verifying Camunda engine");
186             return NOT_STARTED_RESPONSE;
187         }
188
189         return HEALTH_CHECK_RESPONSE;
190     }
191
192
193     private String verifyOldUUID (String oldId) {
194         if (!isValidUUID(oldId)) {
195             String newId = UUID.randomUUID().toString();
196             MsoLogger.setLogContext(newId, null);
197             return newId;
198         }
199         MsoLogger.setLogContext(oldId, null);
200         return oldId;
201     }
202
203
204     private boolean isValidUUID (String id) {
205         try {
206             if (null == id) {
207                 return false;
208             }
209             UUID uuid = UUID.fromString(id);
210             return uuid.toString().equalsIgnoreCase(id);
211         } catch (IllegalArgumentException iae) {
212             return false;
213         }
214     }
215
216     private String decrypt(String encryptedString, String key){
217         try {
218             if (encryptedString != null && !encryptedString.isEmpty() && key != null && !key.isEmpty()) {
219                 return CryptoUtils.decrypt(encryptedString, key);
220             }
221         } catch (Exception e) {
222             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, "Failed to decrypt credentials", e);
223         }
224         return null;
225     }
226
227     private boolean getSiteStatus (String url, String site, String credential, String key) throws Exception {
228         // set the connection timeout value to 30 seconds (30000 milliseconds)
229         RequestConfig.Builder requestBuilder = RequestConfig.custom();
230         requestBuilder = requestBuilder.setConnectTimeout(30000);
231         requestBuilder = requestBuilder.setConnectionRequestTimeout(30000);
232         HttpClientBuilder builder = HttpClientBuilder.create ();
233         builder.setDefaultRequestConfig (requestBuilder.build ());
234
235         HttpPost post = new HttpPost(url);
236
237         String cred = decrypt(credential, key);
238         if (cred != null && !cred.isEmpty()) {
239             post.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString(cred.getBytes()));
240         }
241         msoLogger.debug("Post url is: " + url);
242
243         //now create a soap request message as follows:
244         final StringBuffer payload = new StringBuffer();
245         payload.append("\n");
246         payload.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:req=\"http://org.openecomp.mso/requestsdb\">\n");
247         payload.append("<soapenv:Header/>\n");
248         payload.append("<soapenv:Body>\n");
249         payload.append("<req:getSiteStatus>\n");
250         payload.append("<siteName>" + site + "</siteName>\n");
251         payload.append("</req:getSiteStatus>\n");
252         payload.append("</soapenv:Body>\n");
253         payload.append("</soapenv:Envelope>\n");
254
255         msoLogger.debug ("Initialize SOAP request to url:" + url);
256         msoLogger.debug ("The payload of the request is:" + payload);
257         HttpEntity entity = new StringEntity(payload.toString(),"UTF-8");
258         post.setEntity(entity);
259
260         CloseableHttpClient client = builder.build ();
261         HttpResponse response = client.execute(post);
262         msoLogger.debug("Response received is:" + response);
263
264         int statusCode = response.getStatusLine().getStatusCode();
265         if (statusCode != 200) {
266
267             msoLogger.error(MessageEnum.GENERAL_EXCEPTION_ARG, "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.DataError,
268                     "Communication with DB Adapter failed, The response received from DB Adapter is with failed status code:" + statusCode);
269
270             Exception e = new Exception("Communication with DB Adapter failed");
271             throw e;
272         }
273         BufferedReader rd = new BufferedReader(
274                 new InputStreamReader(response.getEntity().getContent()));
275
276         StringBuffer result = new StringBuffer();
277         String line = "";
278         while ((line = rd.readLine()) != null) {
279             result.append(line);
280         }
281         msoLogger.debug("Content of the response is:" + result);
282         String status = result.substring(result.indexOf("<return>") + 8, result.indexOf("</return>"));
283
284         client.close (); //shut down the connection
285         return Boolean.valueOf(status);
286     }
287 }