2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.pap.xacml.rest;
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 import java.net.ConnectException;
27 import java.net.HttpURLConnection;
28 import java.net.MalformedURLException;
29 import java.net.SocketTimeoutException;
31 import java.net.UnknownHostException;
32 import java.util.HashMap;
33 import java.util.HashSet;
35 import org.onap.policy.common.logging.eelf.MessageCodes;
36 import org.onap.policy.common.logging.eelf.PolicyLogger;
37 import org.onap.policy.common.logging.flexlogger.FlexLogger;
38 import org.onap.policy.common.logging.flexlogger.Logger;
39 import org.onap.policy.pap.xacml.restAuth.CheckPDP;
40 import org.onap.policy.rest.XACMLRestProperties;
41 import org.onap.policy.xacml.api.pap.OnapPDP;
42 import org.onap.policy.xacml.api.pap.OnapPDPGroup;
43 import org.onap.policy.xacml.api.pap.PAPPolicyEngine;
47 * Heartbeat thread - periodically check on PDPs' status.
48 * Heartbeat with all known PDPs.
49 * Implementation note:
50 * The PDPs are contacted Sequentially, not in Parallel.
51 * If we did this in parallel using multiple threads we would simultaneously use - 1 thread and - 1
52 * connection for EACH PDP. This could become a resource problem since we already use multiple
53 * threads and connections for updating the PDPs when user changes occur. Using separate threads can
54 * also make it tricky dealing with timeouts on PDPs that are non-responsive.
55 * The Sequential operation does a heartbeat request to each PDP one at a time. This has the flaw
56 * that any PDPs that do not respond will hold up the entire heartbeat sequence until they timeout.
57 * If there are a lot of non-responsive PDPs and the timeout is large-ish (the default is 20
58 * seconds) it could take a long time to cycle through all of the PDPs. That means that this may not
59 * notice a PDP being down in a predictable time.
61 public class Heartbeat implements Runnable {
63 private static final Logger LOGGER = FlexLogger.getLogger(Heartbeat.class);
65 private PAPPolicyEngine papEngine;
66 private Set<OnapPDP> pdps = new HashSet<>();
67 private int heartbeatInterval;
68 private int heartbeatTimeout;
69 private static final String HEARTBEATSTRING = " Heartbeat '";
70 private static final String XACMLPAPSERVLET = "XacmlPapServlet";
71 private static final String ISRUNNINGFALSE = "isRunning is false, getting out of loop.";
72 private volatile boolean isRunning = false;
74 public synchronized boolean isHeartBeatRunning() {
75 return this.isRunning;
78 public synchronized void terminate() {
79 this.isRunning = false;
83 * Instantiates a new heartbeat.
85 * @param papEngine2 the pap engine 2
87 public Heartbeat(PAPPolicyEngine papEngine2) {
88 papEngine = papEngine2;
89 this.heartbeatInterval =
90 Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_HEARTBEAT_INTERVAL, "10000"));
91 this.heartbeatTimeout =
92 Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_HEARTBEAT_TIMEOUT, "10000"));
97 // Set ourselves as running
99 this.isRunning = true;
102 while (this.isHeartBeatRunning()) {
103 // Wait the given time
104 Thread.sleep(heartbeatInterval);
105 // get the list of PDPs (may have changed since last time)
107 synchronized (papEngine) {
110 // Check for shutdown
111 if (!this.isHeartBeatRunning()) {
112 LOGGER.info(ISRUNNINGFALSE);
116 // Check for shutdown
117 if (!this.isHeartBeatRunning()) {
118 LOGGER.info(ISRUNNINGFALSE);
122 } catch (InterruptedException e) {
123 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR + " Heartbeat interrupted. Shutting down");
125 Thread.currentThread().interrupt();
129 private void getPdpsFromGroup() {
131 for (OnapPDPGroup g : papEngine.getOnapPDPGroups()) {
132 for (OnapPDP p : g.getOnapPdps()) {
136 } catch (PAPException e) {
137 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
138 "Heartbeat unable to read PDPs from PAPEngine");
142 private void notifyEachPdp() {
143 HashMap<String, URL> idToUrlMap = new HashMap<>();
144 for (OnapPDP pdp : pdps) {
145 // Check for shutdown
146 if (!this.isHeartBeatRunning()) {
147 LOGGER.info(ISRUNNINGFALSE);
150 // the id of the PDP is its url (though we add a query
152 URL pdpUrl = idToUrlMap.get(pdp.getId());
153 if (pdpUrl == null) {
154 // haven't seen this PDP before
155 String fullUrlString = null;
158 if (CheckPDP.validateID(pdp.getId())) {
159 fullUrlString = pdp.getId() + "?type=hb";
160 pdpUrl = new URL(fullUrlString);
161 idToUrlMap.put(pdp.getId(), pdpUrl);
163 } catch (MalformedURLException e) {
164 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, XACMLPAPSERVLET,
165 " PDP id '" + fullUrlString + "' is not a valid URL");
168 updatePdpStatus(pdp, openPdpConnection(pdpUrl, pdp));
172 private String openPdpConnection(URL pdpUrl, OnapPDP pdp) {
173 // Do a GET with type HeartBeat
174 String newStatus = "";
175 HttpURLConnection connection = null;
177 // Open up the connection
178 if (pdpUrl != null) {
179 connection = (HttpURLConnection) pdpUrl.openConnection();
180 // Setup our method and headers
181 connection.setRequestMethod("GET");
182 connection.setConnectTimeout(heartbeatTimeout);
184 String encoding = CheckPDP.getEncoding(pdp.getId());
185 if (encoding != null) {
186 connection.setRequestProperty("Authorization", "Basic " + encoding);
189 connection.connect();
190 if (connection.getResponseCode() == 204) {
191 newStatus = connection.getHeaderField(XACMLRestProperties.PROP_PDP_HTTP_HEADER_HB);
192 if (LOGGER.isDebugEnabled()) {
193 LOGGER.debug("Heartbeat '" + pdp.getId() + "' status='" + newStatus + "'");
196 // anything else is an unexpected result
197 newStatus = PDPStatus.Status.UNKNOWN.toString();
198 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR + " Heartbeat connect response code "
199 + connection.getResponseCode() + ": " + pdp.getId());
202 } catch (UnknownHostException e) {
203 newStatus = PDPStatus.Status.NO_SUCH_HOST.toString();
204 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
205 HEARTBEATSTRING + pdp.getId() + "' NO_SUCH_HOST");
206 } catch (SocketTimeoutException e) {
207 newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
208 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
209 HEARTBEATSTRING + pdp.getId() + "' connection timeout");
210 } catch (ConnectException e) {
211 newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
212 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
213 HEARTBEATSTRING + pdp.getId() + "' cannot connect");
214 } catch (Exception e) {
215 newStatus = PDPStatus.Status.UNKNOWN.toString();
216 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
217 HEARTBEATSTRING + pdp.getId() + "' connect exception");
219 // cleanup the connection
220 if (connection != null)
221 connection.disconnect();
226 private void updatePdpStatus(OnapPDP pdp, String newStatus) {
227 if (!pdp.getStatus().getStatus().toString().equals(newStatus)) {
228 if (LOGGER.isDebugEnabled()) {
229 LOGGER.debug("previous status='" + pdp.getStatus().getStatus() + "' new Status='" + newStatus + "'");
232 getPAPInstance().setPDPSummaryStatus(pdp, newStatus);
233 } catch (PAPException e) {
234 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW, e, XACMLPAPSERVLET,
235 "Unable to set state for PDP '" + pdp.getId());
240 private XACMLPapServlet getPAPInstance() {
241 return new XACMLPapServlet();