cd290c66cc60158310993270529eb05f6098d568
[policy/engine.git] / ONAP-PAP-REST / src / main / java / org / onap / policy / pap / xacml / rest / components / NotifyOtherPaps.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP-PAP-REST
4  * ================================================================================
5  * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2019 Nordix Foundation.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.pap.xacml.rest.components;
23
24 import com.att.research.xacml.util.XACMLProperties;
25 import java.net.HttpURLConnection;
26 import java.net.MalformedURLException;
27 import java.net.ProtocolException;
28 import java.net.URL;
29 import java.nio.charset.StandardCharsets;
30 import java.util.ArrayList;
31 import java.util.Base64;
32 import java.util.LinkedList;
33 import java.util.List;
34 import java.util.UUID;
35 import org.onap.policy.common.logging.flexlogger.FlexLogger;
36 import org.onap.policy.common.logging.flexlogger.Logger;
37 import org.onap.policy.rest.XacmlRestProperties;
38 import org.onap.policy.rest.jpa.PolicyDBDaoEntity;
39 import org.onap.policy.utils.PeCryptoUtils;
40
41 public class NotifyOtherPaps {
42
43     private static final Logger LOGGER = FlexLogger.getLogger(NotifyOtherPaps.class);
44     private List<PolicyDBDaoEntity> failedPaps = null;
45
46     public void notifyOthers(long entityId, String entityType) {
47         notifyOthers(entityId, entityType, null);
48     }
49
50     /**
51      * Notify others.
52      *
53      * @param entityId the entity id
54      * @param entityType the entity type
55      * @param newGroupId the new group id
56      */
57     public void notifyOthers(long entityId, String entityType, String newGroupId) {
58         LOGGER.debug("notifyOthers(long entityId, String entityType, long newGroupId) as notifyOthers(" + entityId + ","
59             + entityType + "," + newGroupId + ") called");
60         failedPaps = new ArrayList<>();
61
62         List<?> otherServers = PolicyDbDao.getPolicyDbDaoInstance().getOtherServers();
63         // Notify other paps
64         startNotifyThreads(otherServers, entityId, entityType, newGroupId);
65         // Retry for failed paps
66         if (!failedPaps.isEmpty()) {
67             startNotifyThreads(failedPaps, entityId, entityType, newGroupId);
68         }
69     }
70
71     protected void startNotifyThreads(List<?> otherServers, long entityId, String entityType, String newGroupId) {
72         LinkedList<Thread> notifyThreads = new LinkedList<>();
73         // we're going to run notifications in parallel threads to speed things
74         // up
75         for (Object obj : otherServers) {
76             Thread newNotifyThread = new Thread(new NotifyOtherThread(obj, entityId, entityType, newGroupId));
77             newNotifyThread.start();
78             notifyThreads.add(newNotifyThread);
79         }
80         // we want to wait for all notifications to complete or timeout before
81         // we unlock the interface and allow more changes
82         for (Thread t : notifyThreads) {
83             try {
84                 t.join();
85             } catch (Exception e) {
86                 LOGGER.warn("Could not join a notifcation thread" + e);
87             }
88         }
89     }
90
91     private class NotifyOtherThread implements Runnable {
92         public NotifyOtherThread(Object obj, long entityId, String entityType, String newGroupId) {
93             this.obj = obj;
94             this.entityId = entityId;
95             this.entityType = entityType;
96             this.newGroupId = newGroupId;
97         }
98
99         private Object obj;
100         private long entityId;
101         private String entityType;
102         private String newGroupId;
103
104         @Override
105         public void run() {
106             PolicyDbDao dao = new PolicyDbDao();
107             PolicyDBDaoEntity dbdEntity = (PolicyDBDaoEntity) obj;
108             String otherPap = dbdEntity.getPolicyDBDaoUrl();
109             String txt;
110             try {
111                 txt = PeCryptoUtils.decrypt(dbdEntity.getPassword());
112             } catch (Exception e) {
113                 LOGGER.debug(e);
114                 // if we can't decrypt, might as well try it anyway
115                 txt = dbdEntity.getPassword();
116             }
117
118             HttpURLConnection connection = null;
119             URL url;
120             String papUrl;
121             try {
122                 String[] papUrlUserPass = dao.getPapUrlUserPass();
123                 if (papUrlUserPass == null) {
124                     papUrl = "undefined";
125                 } else {
126                     papUrl = papUrlUserPass[0];
127                 }
128                 LOGGER.debug("We are going to try to notify " + otherPap);
129                 // is this our own url?
130                 String ourUrl = otherPap;
131                 try {
132                     ourUrl = dao.splitPapUrlUserPass(otherPap)[0];
133                 } catch (Exception e) {
134                     ourUrl = otherPap;
135                     LOGGER.debug(e);
136                 }
137                 if (otherPap == null) {
138                     otherPap = "undefined";
139                 }
140                 if (papUrl.equals(ourUrl)) {
141                     LOGGER.debug(otherPap + " is our url, skipping notify");
142                     return;
143                 }
144                 if (newGroupId == null) {
145                     url = new URL(
146                         otherPap + "?policydbdaourl=" + papUrl + "&entityid=" + entityId + "&entitytype=" + entityType);
147                 } else {
148                     url = new URL(otherPap + "?policydbdaourl=" + papUrl + "&entityid=" + entityId + "&entitytype="
149                         + entityType + "&extradata=" + newGroupId);
150                 }
151             } catch (MalformedURLException e) {
152                 LOGGER.warn("Caught MalformedURLException on: new URL()", e);
153                 return;
154             }
155             //
156             // Open up the connection
157             //
158             LOGGER.info("PolicyDBDao: NotifyOtherThread: notifying other PAPs of an update");
159             LOGGER.info("Connecting with url: " + url);
160             try {
161                 connection = (HttpURLConnection) url.openConnection();
162             } catch (Exception e) {
163                 LOGGER.warn("Caught exception on: url.openConnection()", e);
164                 return;
165             }
166             //
167             // Setup our method and headers
168             //
169             try {
170                 connection.setRequestMethod("PUT");
171             } catch (ProtocolException e) {
172                 // why would this error ever occur?
173                 LOGGER.warn("Caught ProtocolException on connection.setRequestMethod(\"PUT\");", e);
174                 return;
175             }
176
177             String username = dbdEntity.getUsername();
178             UUID requestId = UUID.randomUUID();
179
180             Base64.Encoder encoder = Base64.getEncoder();
181             String encoding = encoder.encodeToString((username + ":" + txt).getBytes(StandardCharsets.UTF_8));
182             connection.setRequestProperty("Authorization", "Basic " + encoding);
183             connection.setRequestProperty("Accept", "text/x-java-properties");
184             connection.setRequestProperty("Content-Type", "text/x-java-properties");
185             connection.setRequestProperty("requestID", requestId.toString());
186             int readTimeout;
187             try {
188                 readTimeout =
189                     Integer.parseInt(XACMLProperties.getProperty(XacmlRestProperties.PROP_PAP_NOTIFY_TIMEOUT));
190             } catch (Exception e) {
191                 LOGGER.error("xacml.rest.pap.notify.timeoutms property not set, using a default.", e);
192                 readTimeout = 10000;
193             }
194             connection.setReadTimeout(readTimeout);
195             connection.setConnectTimeout(readTimeout);
196             connection.setUseCaches(false);
197             //
198             // Adding this in. It seems the HttpUrlConnection class does NOT
199             // properly forward our headers for POST re-direction. It does so
200             // for a GET re-direction.
201             //
202             // So we need to handle this ourselves.
203             //
204             connection.setInstanceFollowRedirects(false);
205             connection.setDoOutput(true);
206             connection.setDoInput(true);
207             try {
208                 connection.connect();
209             } catch (Exception e) {
210                 LOGGER.warn("Caught exception on: connection.connect()", e);
211                 return;
212             }
213             try {
214                 if (connection.getResponseCode() == 200) {
215                     LOGGER.info("PolicyDBDao: NotifyOtherThread received response 200 from pap server on notify");
216                 } else {
217                     LOGGER.warn("PolicyDBDao: NotifyOtherThread connection response code not 200, received: "
218                         + connection.getResponseCode());
219                     failedPaps.add(dbdEntity);
220                 }
221             } catch (Exception e) {
222                 LOGGER.warn("Caught Exception on: connection.getResponseCode() ", e);
223             }
224
225             connection.disconnect();
226         }
227     }
228 }