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;
27 import java.net.ConnectException;
28 import java.net.HttpURLConnection;
29 import java.net.MalformedURLException;
30 import java.net.SocketTimeoutException;
32 import java.net.UnknownHostException;
33 import java.util.HashMap;
34 import java.util.HashSet;
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;
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.
62 public class Heartbeat implements Runnable {
64 private static final Logger LOGGER = FlexLogger.getLogger(Heartbeat.class);
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;
75 public synchronized boolean isHeartBeatRunning() {
76 return this.isRunning;
79 public synchronized void terminate() {
80 this.isRunning = false;
84 * Instantiates a new heartbeat.
86 * @param papEngine2 the pap engine 2
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"));
98 // Set ourselves as running
100 this.isRunning = true;
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)
108 synchronized (papEngine) {
111 // Check for shutdown
112 if (!this.isHeartBeatRunning()) {
113 LOGGER.info(ISRUNNINGFALSE);
117 // Check for shutdown
118 if (!this.isHeartBeatRunning()) {
119 LOGGER.info(ISRUNNINGFALSE);
123 } catch (InterruptedException e) {
124 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR + " Heartbeat interrupted. Shutting down");
126 Thread.currentThread().interrupt();
130 private void getPdpsFromGroup() {
132 for (OnapPDPGroup g : papEngine.getOnapPDPGroups()) {
133 for (OnapPDP p : g.getOnapPdps()) {
137 } catch (PAPException e) {
138 PolicyLogger.error(MessageCodes.ERROR_SYSTEM_ERROR, e, XACMLPAPSERVLET,
139 "Heartbeat unable to read PDPs from PAPEngine");
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);
151 // the id of the PDP is its url (though we add a query
153 URL pdpUrl = idToUrlMap.get(pdp.getId());
154 if (pdpUrl == null) {
155 // haven't seen this PDP before
156 String fullUrlString = null;
159 if (CheckPDP.validateID(pdp.getId())) {
160 fullUrlString = pdp.getId() + "?type=hb";
161 pdpUrl = new URL(fullUrlString);
162 idToUrlMap.put(pdp.getId(), pdpUrl);
164 } catch (MalformedURLException e) {
165 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, XACMLPAPSERVLET,
166 " PDP id '" + fullUrlString + "' is not a valid URL");
169 updatePdpStatus(pdp, openPdpConnection(pdpUrl, pdp));
173 private String openPdpConnection(URL pdpUrl, OnapPDP pdp) {
174 // Do a GET with type HeartBeat
175 String newStatus = "";
176 HttpURLConnection connection = null;
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);
185 String encoding = CheckPDP.getEncoding(pdp.getId());
186 if (encoding != null) {
187 connection.setRequestProperty("Authorization", "Basic " + encoding);
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 + "'");
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());
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");
220 // cleanup the connection
221 if (connection != null)
222 connection.disconnect();
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 + "'");
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());
241 private XACMLPapServlet getPAPInstance() {
242 return new XACMLPapServlet();