CHeckstyle and JUnit for base package in ONAP-REST
[policy/engine.git] / ONAP-PAP-REST / src / main / java / org / onap / policy / pap / xacml / rest / Heartbeat.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP-PAP-REST
4  * ================================================================================
5  * Copyright (C) 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.pap.xacml.rest;
22
23 import com.att.research.xacml.api.pap.PAPException;
24 import com.att.research.xacml.api.pap.PDPStatus;
25 import com.att.research.xacml.util.XACMLProperties;
26
27 import java.net.ConnectException;
28 import java.net.HttpURLConnection;
29 import java.net.MalformedURLException;
30 import java.net.SocketTimeoutException;
31 import java.net.URL;
32 import java.net.UnknownHostException;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Set;
36
37 import org.onap.policy.common.logging.eelf.MessageCodes;
38 import org.onap.policy.common.logging.eelf.PolicyLogger;
39 import org.onap.policy.common.logging.flexlogger.FlexLogger;
40 import org.onap.policy.common.logging.flexlogger.Logger;
41 import org.onap.policy.pap.xacml.restAuth.CheckPDP;
42 import org.onap.policy.rest.XacmlRestProperties;
43 import org.onap.policy.xacml.api.pap.OnapPDP;
44 import org.onap.policy.xacml.api.pap.OnapPDPGroup;
45 import org.onap.policy.xacml.api.pap.PAPPolicyEngine;
46
47 /**
48  * Heartbeat thread - periodically check on PDPs' status.
49  * Heartbeat with all known PDPs.
50  * Implementation note:
51  * The PDPs are contacted Sequentially, not in Parallel.
52  * If we did this in parallel using multiple threads we would simultaneously use - 1 thread and - 1
53  * connection for EACH PDP. This could become a resource problem since we already use multiple
54  * threads and connections for updating the PDPs when user changes occur. Using separate threads can
55  * also make it tricky dealing with timeouts on PDPs that are non-responsive.
56  * The Sequential operation does a heartbeat request to each PDP one at a time. This has the flaw
57  * that any PDPs that do not respond will hold up the entire heartbeat sequence until they timeout.
58  * If there are a lot of non-responsive PDPs and the timeout is large-ish (the default is 20
59  * seconds) it could take a long time to cycle through all of the PDPs. That means that this may not
60  * notice a PDP being down in a predictable time.
61  */
62 public class Heartbeat implements Runnable {
63
64     private static final Logger LOGGER = FlexLogger.getLogger(Heartbeat.class);
65
66     private PAPPolicyEngine papEngine;
67     private Set<OnapPDP> pdps = new HashSet<>();
68     private int heartbeatInterval;
69     private int heartbeatTimeout;
70     private static final String HEARTBEATSTRING = " Heartbeat '";
71     private static final String XACMLPAPSERVLET = "XacmlPapServlet";
72     private static final String ISRUNNINGFALSE = "isRunning is false, getting out of loop.";
73     private volatile boolean isRunning = false;
74
75     public synchronized boolean isHeartBeatRunning() {
76         return this.isRunning;
77     }
78
79     public synchronized void terminate() {
80         this.isRunning = false;
81     }
82
83     /**
84      * Instantiates a new heartbeat.
85      *
86      * @param papEngine2 the pap engine 2
87      */
88     public Heartbeat(PAPPolicyEngine papEngine2) {
89         papEngine = papEngine2;
90         this.heartbeatInterval =
91                 Integer.parseInt(XACMLProperties.getProperty(XacmlRestProperties.PROP_PAP_HEARTBEAT_INTERVAL, "10000"));
92         this.heartbeatTimeout =
93                 Integer.parseInt(XACMLProperties.getProperty(XacmlRestProperties.PROP_PAP_HEARTBEAT_TIMEOUT, "10000"));
94     }
95
96     @Override
97     public void run() {
98         // Set ourselves as running
99         synchronized (this) {
100             this.isRunning = true;
101         }
102         try {
103             while (this.isHeartBeatRunning()) {
104                 // Wait the given time
105                 Thread.sleep(heartbeatInterval);
106                 // get the list of PDPs (may have changed since last time)
107                 pdps.clear();
108                 synchronized (papEngine) {
109                     getPdpsFromGroup();
110                 }
111                 // Check for shutdown
112                 if (!this.isHeartBeatRunning()) {
113                     LOGGER.info(ISRUNNINGFALSE);
114                     break;
115                 }
116                 notifyEachPdp();
117                 // Check for shutdown
118                 if (!this.isHeartBeatRunning()) {
119                     LOGGER.info(ISRUNNINGFALSE);
120                     break;
121                 }
122             }
123         } catch (InterruptedException e) {
124             PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR + " Heartbeat interrupted.  Shutting down");
125             this.terminate();
126             Thread.currentThread().interrupt();
127         }
128     }
129
130     private void getPdpsFromGroup() {
131         try {
132             for (OnapPDPGroup g : papEngine.getOnapPDPGroups()) {
133                 for (OnapPDP p : g.getOnapPdps()) {
134                     pdps.add(p);
135                 }
136             }
137         } catch (PAPException e) {
138             PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
139                     "Heartbeat unable to read PDPs from PAPEngine");
140         }
141     }
142
143     private void notifyEachPdp() {
144         HashMap<String, URL> idToUrlMap = new HashMap<>();
145         for (OnapPDP pdp : pdps) {
146             // Check for shutdown
147             if (!this.isHeartBeatRunning()) {
148                 LOGGER.info(ISRUNNINGFALSE);
149                 break;
150             }
151             // the id of the PDP is its url (though we add a query
152             // parameter)
153             URL pdpUrl = idToUrlMap.get(pdp.getId());
154             if (pdpUrl == null) {
155                 // haven't seen this PDP before
156                 String fullUrlString = null;
157                 try {
158                     // Check PDP ID
159                     if (CheckPDP.validateID(pdp.getId())) {
160                         fullUrlString = pdp.getId() + "?type=hb";
161                         pdpUrl = new URL(fullUrlString);
162                         idToUrlMap.put(pdp.getId(), pdpUrl);
163                     }
164                 } catch (MalformedURLException e) {
165                     PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, XACMLPAPSERVLET,
166                             " PDP id '" + fullUrlString + "' is not a valid URL");
167                 }
168             }
169             updatePdpStatus(pdp, openPdpConnection(pdpUrl, pdp));
170         }
171     }
172
173     private String openPdpConnection(URL pdpUrl, OnapPDP pdp) {
174         // Do a GET with type HeartBeat
175         String newStatus = "";
176         HttpURLConnection connection = null;
177         try {
178             // Open up the connection
179             if (pdpUrl != null) {
180                 connection = (HttpURLConnection) pdpUrl.openConnection();
181                 // Setup our method and headers
182                 connection.setRequestMethod("GET");
183                 connection.setConnectTimeout(heartbeatTimeout);
184                 // Authentication
185                 String encoding = CheckPDP.getEncoding(pdp.getId());
186                 if (encoding != null) {
187                     connection.setRequestProperty("Authorization", "Basic " + encoding);
188                 }
189                 // Do the connect
190                 connection.connect();
191                 if (connection.getResponseCode() == 204) {
192                     newStatus = connection.getHeaderField(XacmlRestProperties.PROP_PDP_HTTP_HEADER_HB);
193                     if (LOGGER.isDebugEnabled()) {
194                         LOGGER.debug("Heartbeat '" + pdp.getId() + "' status='" + newStatus + "'");
195                     }
196                 } else {
197                     // anything else is an unexpected result
198                     newStatus = PDPStatus.Status.UNKNOWN.toString();
199                     PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR + " Heartbeat connect response code "
200                             + connection.getResponseCode() + ": " + pdp.getId());
201                 }
202             }
203         } catch (UnknownHostException e) {
204             newStatus = PDPStatus.Status.NO_SUCH_HOST.toString();
205             PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
206                     HEARTBEATSTRING + pdp.getId() + "' NO_SUCH_HOST");
207         } catch (SocketTimeoutException e) {
208             newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
209             PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
210                     HEARTBEATSTRING + pdp.getId() + "' connection timeout");
211         } catch (ConnectException e) {
212             newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
213             PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
214                     HEARTBEATSTRING + pdp.getId() + "' cannot connect");
215         } catch (Exception e) {
216             newStatus = PDPStatus.Status.UNKNOWN.toString();
217             PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
218                     HEARTBEATSTRING + pdp.getId() + "' connect exception");
219         } finally {
220             // cleanup the connection
221             if (connection != null)
222                 connection.disconnect();
223         }
224         return newStatus;
225     }
226
227     private void updatePdpStatus(OnapPDP pdp, String newStatus) {
228         if (!pdp.getStatus().getStatus().toString().equals(newStatus)) {
229             if (LOGGER.isDebugEnabled()) {
230                 LOGGER.debug("previous status='" + pdp.getStatus().getStatus() + "'  new Status='" + newStatus + "'");
231             }
232             try {
233                 getPAPInstance().setPDPSummaryStatus(pdp, newStatus);
234             } catch (PAPException e) {
235                 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, XACMLPAPSERVLET,
236                         "Unable to set state for PDP '" + pdp.getId());
237             }
238         }
239     }
240
241     private XACMLPapServlet getPAPInstance() {
242         return new XACMLPapServlet();
243     }
244
245 }