update link to upper-constraints.txt
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / SubscriptionServlet.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * *\r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * *\r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 \r
24 \r
25 package org.onap.dmaap.datarouter.provisioning;\r
26 \r
27 import static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;\r
28 \r
29 import com.att.eelf.configuration.EELFLogger;\r
30 import com.att.eelf.configuration.EELFManager;\r
31 import java.io.IOException;\r
32 import java.io.InvalidObjectException;\r
33 import java.net.HttpURLConnection;\r
34 import java.net.URL;\r
35 import java.util.ArrayList;\r
36 import java.util.List;\r
37 import jakarta.servlet.http.HttpServletRequest;\r
38 import jakarta.servlet.http.HttpServletResponse;\r
39 import org.json.JSONException;\r
40 import org.json.JSONObject;\r
41 import org.onap.dmaap.datarouter.authz.AuthorizationResponse;\r
42 import org.onap.dmaap.datarouter.provisioning.beans.EventLogRecord;\r
43 import org.onap.dmaap.datarouter.provisioning.beans.Subscription;\r
44 import org.onap.dmaap.datarouter.provisioning.eelf.EelfMsgs;\r
45 import org.onap.dmaap.datarouter.provisioning.utils.SynchronizerTask;\r
46 \r
47 /**\r
48  * This servlet handles provisioning for the <subscriptionURL> which is generated by the provisioning server to\r
49  * handle the inspection, modification, and deletion of a particular subscription to a feed. It supports DELETE to\r
50  * delete a subscription, GET to retrieve information about the subscription, and PUT to modify the subscription.  In DR\r
51  * 3.0, POST is also supported in order to reset the subscription timers for individual subscriptions.\r
52  *\r
53  * @author Robert Eby\r
54  * @version $Id$\r
55  */\r
56 public class SubscriptionServlet extends ProxyServlet {\r
57 \r
58     private static final String SUBCNTRL_CONTENT_TYPE = "application/vnd.dmaap-dr.subscription-control";\r
59     //Adding EELF Logger Rally:US664892\r
60     private static final EELFLogger eelfLogger = EELFManager.getInstance()\r
61         .getLogger(SubscriptionServlet.class);\r
62 \r
63     /**\r
64      * DELETE on the &lt;subscriptionUrl&gt; -- delete a subscription. See the <i>Deleting a Subscription</i> section in\r
65      * the <b>Provisioning API</b> document for details on how this method should be invoked.\r
66      */\r
67     @Override\r
68     public void doDelete(HttpServletRequest req, HttpServletResponse resp) {\r
69         setIpFqdnRequestIDandInvocationIDForEelf("doDelete", req);\r
70         eelfLogger.info(EelfMsgs.ENTRY);\r
71         try {\r
72             eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID,\r
73                     req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");\r
74             EventLogRecord elr = new EventLogRecord(req);\r
75             String message = isAuthorizedForProvisioning(req);\r
76             if (message != null) {\r
77                 elr.setMessage(message);\r
78                 elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
79                 eventlogger.error(elr.toString());\r
80                 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, message, eventlogger);\r
81                 return;\r
82             }\r
83             if (isProxyServer()) {\r
84                 super.doDelete(req, resp);\r
85                 return;\r
86             }\r
87             String bhdr = req.getHeader(BEHALF_HEADER);\r
88             if (bhdr == null) {\r
89                 message = MISSING_ON_BEHALF;\r
90                 elr.setMessage(message);\r
91                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
92                 eventlogger.error(elr.toString());\r
93                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
94                 return;\r
95             }\r
96             int subid = getIdFromPath(req);\r
97             if (subid < 0) {\r
98                 message = BAD_SUB;\r
99                 elr.setMessage(message);\r
100                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
101                 eventlogger.error(elr.toString());\r
102                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
103                 return;\r
104             }\r
105             Subscription sub = Subscription.getSubscriptionById(subid);\r
106             if (sub == null) {\r
107                 message = BAD_SUB;\r
108                 elr.setMessage(message);\r
109                 elr.setResult(HttpServletResponse.SC_NOT_FOUND);\r
110                 eventlogger.error(elr.toString());\r
111                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, message, eventlogger);\r
112                 return;\r
113             }\r
114             // Delete Subscription\r
115             if (doDelete(sub)) {\r
116                 activeSubs--;\r
117                 // send response\r
118                 elr.setResult(HttpServletResponse.SC_NO_CONTENT);\r
119                 eventlogger.info(elr.toString());\r
120                 resp.setStatus(HttpServletResponse.SC_NO_CONTENT);\r
121                 provisioningDataChanged();\r
122             } else {\r
123                 // Something went wrong with the DELETE\r
124                 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
125                 eventlogger.error(elr.toString());\r
126                 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, intlogger);\r
127             }\r
128         } finally {\r
129             eelfLogger.info(EelfMsgs.EXIT);\r
130         }\r
131     }\r
132 \r
133     /**\r
134      * GET on the &lt;subscriptionUrl&gt; -- get information about a subscription. See the <i>Retreiving Information\r
135      * about a Subscription</i> section in the <b>Provisioning API</b> document for details on how this method should be\r
136      * invoked.\r
137      */\r
138     @Override\r
139     public void doGet(HttpServletRequest req, HttpServletResponse resp) {\r
140         setIpFqdnRequestIDandInvocationIDForEelf("doGet", req);\r
141         eelfLogger.info(EelfMsgs.ENTRY);\r
142         try {\r
143             eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID,\r
144                     req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");\r
145             EventLogRecord elr = new EventLogRecord(req);\r
146             String message = isAuthorizedForProvisioning(req);\r
147             if (message != null) {\r
148                 elr.setMessage(message);\r
149                 elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
150                 eventlogger.error(elr.toString());\r
151                 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, message, eventlogger);\r
152                 return;\r
153             }\r
154             if (isProxyServer()) {\r
155                 super.doGet(req, resp);\r
156                 return;\r
157             }\r
158             String bhdr = req.getHeader(BEHALF_HEADER);\r
159             if (bhdr == null) {\r
160                 message = MISSING_ON_BEHALF;\r
161                 elr.setMessage(message);\r
162                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
163                 eventlogger.error(elr.toString());\r
164                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
165                 return;\r
166             }\r
167             int subid = getIdFromPath(req);\r
168             if (subid < 0) {\r
169                 message = BAD_SUB;\r
170                 elr.setMessage(message);\r
171                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
172                 eventlogger.error(elr.toString());\r
173                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
174                 return;\r
175             }\r
176             Subscription sub = Subscription.getSubscriptionById(subid);\r
177             if (sub == null) {\r
178                 message = BAD_SUB;\r
179                 elr.setMessage(message);\r
180                 elr.setResult(HttpServletResponse.SC_NOT_FOUND);\r
181                 eventlogger.error(elr.toString());\r
182                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, message, eventlogger);\r
183                 return;\r
184             }\r
185             // Check with the Authorizer\r
186             AuthorizationResponse aresp = authz.decide(req);\r
187             if (!aresp.isAuthorized()) {\r
188                 message = POLICY_ENGINE;\r
189                 elr.setMessage(message);\r
190                 elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
191                 eventlogger.error(elr.toString());\r
192                 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, message, eventlogger);\r
193                 return;\r
194             }\r
195 \r
196             // send response\r
197             elr.setResult(HttpServletResponse.SC_OK);\r
198             eventlogger.info(elr.toString());\r
199             resp.setStatus(HttpServletResponse.SC_OK);\r
200             resp.setContentType(SUBFULL_CONTENT_TYPE);\r
201             try {\r
202                 resp.getOutputStream().print(sub.asJSONObject(true).toString());\r
203             } catch (IOException ioe) {\r
204                 eventlogger.error("PROV0191 SubscriptionServlet.doGet: " + ioe.getMessage(), ioe);\r
205             }\r
206         } finally {\r
207             eelfLogger.info(EelfMsgs.EXIT);\r
208         }\r
209     }\r
210 \r
211     /**\r
212      * PUT on the &lt;subscriptionUrl&gt; -- modify a subscription. See the <i>Modifying a Subscription</i> section in\r
213      * the <b>Provisioning API</b> document for details on how this method should be invoked.\r
214      */\r
215     @Override\r
216     public void doPut(HttpServletRequest req, HttpServletResponse resp) {\r
217         setIpFqdnRequestIDandInvocationIDForEelf("doPut", req);\r
218         eelfLogger.info(EelfMsgs.ENTRY);\r
219         try {\r
220             eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF_AND_SUBID,\r
221                     req.getHeader(BEHALF_HEADER), getIdFromPath(req) + "");\r
222             EventLogRecord elr = new EventLogRecord(req);\r
223             String message = isAuthorizedForProvisioning(req);\r
224             if (message != null) {\r
225                 elr.setMessage(message);\r
226                 elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
227                 eventlogger.error(elr.toString());\r
228                 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, message, eventlogger);\r
229                 return;\r
230             }\r
231             if (isProxyServer()) {\r
232                 super.doPut(req, resp);\r
233                 return;\r
234             }\r
235             String bhdr = req.getHeader(BEHALF_HEADER);\r
236             if (bhdr == null) {\r
237                 message = MISSING_ON_BEHALF;\r
238                 elr.setMessage(message);\r
239                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
240                 eventlogger.error(elr.toString());\r
241                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
242                 return;\r
243             }\r
244             int subid = getIdFromPath(req);\r
245             if (subid < 0) {\r
246                 message = BAD_SUB;\r
247                 elr.setMessage(message);\r
248                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
249                 eventlogger.error(elr.toString());\r
250                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
251                 return;\r
252             }\r
253             Subscription oldsub = Subscription.getSubscriptionById(subid);\r
254             if (oldsub == null) {\r
255                 message = BAD_SUB;\r
256                 elr.setMessage(message);\r
257                 elr.setResult(HttpServletResponse.SC_NOT_FOUND);\r
258                 eventlogger.error(elr.toString());\r
259                 sendResponseError(resp, HttpServletResponse.SC_NOT_FOUND, message, eventlogger);\r
260                 return;\r
261             }\r
262             // check content type is SUB_CONTENT_TYPE, version 1.0\r
263             ContentHeader ch = getContentHeader(req);\r
264             String ver = ch.getAttribute("version");\r
265             if (!ch.getType().equals(SUB_BASECONTENT_TYPE) || !("1.0".equals(ver) || "2.0".equals(ver))) {\r
266                 message = "Incorrect content-type";\r
267                 elr.setMessage(message);\r
268                 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);\r
269                 eventlogger.error(elr.toString());\r
270                 sendResponseError(resp, HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message, eventlogger);\r
271                 return;\r
272             }\r
273             JSONObject jo = getJSONfromInput(req);\r
274             if (jo == null) {\r
275                 message = BAD_JSON;\r
276                 elr.setMessage(message);\r
277                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
278                 eventlogger.error(elr.toString());\r
279                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
280                 return;\r
281             }\r
282             Subscription sub;\r
283             try {\r
284                 sub = new Subscription(jo);\r
285             } catch (InvalidObjectException e) {\r
286                 message = e.getMessage();\r
287                 elr.setMessage(message);\r
288                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
289                 eventlogger.error(elr.toString(), e);\r
290                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
291                 return;\r
292             }\r
293             sub.setSubid(oldsub.getSubid());\r
294             sub.setFeedid(oldsub.getFeedid());\r
295             sub.setSubscriber(bhdr);    // set from X-DMAAP-DR-ON-BEHALF-OF header\r
296             //Adding for group feature:Rally US708115\r
297             String subjectgroup = (req.getHeader("X-DMAAP-DR-ON-BEHALF-OF-GROUP"));\r
298             if (!oldsub.getSubscriber().equals(sub.getSubscriber()) && subjectgroup == null) {\r
299                 message = "This subscriber must be modified by the same subscriber that created it.";\r
300                 elr.setMessage(message);\r
301                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
302                 eventlogger.error(elr.toString());\r
303                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
304                 return;\r
305             }\r
306 \r
307             // Update SUBSCRIPTIONS table entries\r
308             if (doUpdate(sub)) {\r
309                 // send response\r
310                 elr.setResult(HttpServletResponse.SC_OK);\r
311                 eventlogger.info(elr.toString());\r
312                 resp.setStatus(HttpServletResponse.SC_OK);\r
313                 resp.setContentType(SUBFULL_CONTENT_TYPE);\r
314                 try {\r
315                     resp.getOutputStream().print(sub.asLimitedJSONObject().toString());\r
316                 } catch (IOException ioe) {\r
317                     eventlogger.error("PROV0192 SubscriptionServlet.doPut: " + ioe.getMessage(), ioe);\r
318                 }\r
319 \r
320                 /**Change Owner ship of Subscriber.     Adding for group feature:Rally US708115*/\r
321                 if (jo.has("changeowner") && subjectgroup != null) {\r
322                     try {\r
323                         Boolean changeowner = (Boolean) jo.get("changeowner");\r
324                         if (changeowner != null && changeowner.equals(true)) {\r
325                             sub.setSubscriber(req.getHeader(BEHALF_HEADER));\r
326                             sub.changeOwnerShip();\r
327                         }\r
328                     } catch (JSONException je) {\r
329                         eventlogger.error("PROV0193 SubscriptionServlet.doPut: " + je.getMessage(), je);\r
330                     }\r
331                 }\r
332                 /***End of change ownership.*/\r
333 \r
334                 provisioningDataChanged();\r
335             } else {\r
336                 // Something went wrong with the UPDATE\r
337                 elr.setResult(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
338                 eventlogger.error(elr.toString());\r
339                 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, intlogger);\r
340             }\r
341         } finally {\r
342             eelfLogger.info(EelfMsgs.EXIT);\r
343         }\r
344     }\r
345 \r
346     /**\r
347      * POST on the &lt;subscriptionUrl&gt; -- control a subscription. See the <i>Resetting a Subscription's Retry\r
348      * Schedule</i> section in the <b>Provisioning API</b> document for details on how this method should be invoked.\r
349      */\r
350     @Override\r
351     public void doPost(HttpServletRequest req, HttpServletResponse resp) {\r
352 \r
353         setIpFqdnRequestIDandInvocationIDForEelf("doPost", req);\r
354         eelfLogger.info(EelfMsgs.ENTRY);\r
355         try {\r
356             eelfLogger.info(EelfMsgs.MESSAGE_WITH_BEHALF, req.getHeader(BEHALF_HEADER));\r
357             EventLogRecord elr = new EventLogRecord(req);\r
358             String message = isAuthorizedForProvisioning(req);\r
359             if (message != null) {\r
360                 elr.setMessage(message);\r
361                 elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
362                 eventlogger.error(elr.toString());\r
363                 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, message, eventlogger);\r
364                 return;\r
365             }\r
366             if (isProxyServer()) {\r
367                 super.doPost(req, resp);\r
368                 return;\r
369             }\r
370             String bhdr = req.getHeader(BEHALF_HEADER);\r
371             if (bhdr == null) {\r
372                 message = MISSING_ON_BEHALF;\r
373                 elr.setMessage(message);\r
374                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
375                 eventlogger.error(elr.toString());\r
376                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
377                 return;\r
378             }\r
379             final int subid = getIdFromPath(req);\r
380             if (subid < 0 || Subscription.getSubscriptionById(subid) == null) {\r
381                 message = BAD_SUB;\r
382                 elr.setMessage(message);\r
383                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
384                 eventlogger.error(elr.toString());\r
385                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
386                 return;\r
387             }\r
388             // check content type is SUBCNTRL_CONTENT_TYPE, version 1.0\r
389             ContentHeader ch = getContentHeader(req);\r
390             String ver = ch.getAttribute("version");\r
391             if (!ch.getType().equals(SUBCNTRL_CONTENT_TYPE) || !"1.0".equals(ver)) {\r
392                 message = "Incorrect content-type";\r
393                 elr.setMessage(message);\r
394                 elr.setResult(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);\r
395                 eventlogger.error(elr.toString());\r
396                 sendResponseError(resp, HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, message, eventlogger);\r
397                 return;\r
398             }\r
399             // Check with the Authorizer\r
400             AuthorizationResponse aresp = authz.decide(req);\r
401             if (!aresp.isAuthorized()) {\r
402                 message = POLICY_ENGINE;\r
403                 elr.setMessage(message);\r
404                 elr.setResult(HttpServletResponse.SC_FORBIDDEN);\r
405                 eventlogger.error(elr.toString());\r
406                 sendResponseError(resp, HttpServletResponse.SC_FORBIDDEN, message, eventlogger);\r
407                 return;\r
408             }\r
409             JSONObject jo = getJSONfromInput(req);\r
410             if (jo == null) {\r
411                 message = BAD_JSON;\r
412                 elr.setMessage(message);\r
413                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
414                 eventlogger.error(elr.toString());\r
415                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
416                 return;\r
417             }\r
418             try {\r
419                 // Only the active POD sends notifications\r
420                 boolean active = SynchronizerTask.getSynchronizer().isActive();\r
421                 boolean bool = jo.getBoolean("failed");\r
422                 if (active && !bool) {\r
423                     // Notify all nodes to reset the subscription\r
424                     SubscriberNotifyThread thread = new SubscriberNotifyThread();\r
425                     thread.resetSubscription(subid);\r
426                     thread.start();\r
427                 }\r
428                 // send response\r
429                 elr.setResult(HttpServletResponse.SC_ACCEPTED);\r
430                 eventlogger.info(elr.toString());\r
431                 resp.setStatus(HttpServletResponse.SC_ACCEPTED);\r
432             } catch (JSONException e) {\r
433                 message = BAD_JSON;\r
434                 elr.setMessage(message);\r
435                 elr.setResult(HttpServletResponse.SC_BAD_REQUEST);\r
436                 eventlogger.error(elr.toString(), e);\r
437                 sendResponseError(resp, HttpServletResponse.SC_BAD_REQUEST, message, eventlogger);\r
438             }\r
439         } finally {\r
440             eelfLogger.info(EelfMsgs.EXIT);\r
441         }\r
442     }\r
443 \r
444     /**\r
445      * A Thread class used to serially send reset notifications to all nodes in the DR network, when a POST is received\r
446      * for a subscription.\r
447      */\r
448     public static class SubscriberNotifyThread extends Thread {\r
449 \r
450         static final String URL_TEMPLATE = "http://%s/internal/resetSubscription/%d";\r
451         private List<String> urls = new ArrayList<>();\r
452 \r
453         SubscriberNotifyThread() {\r
454             setName("SubscriberNotifyThread");\r
455         }\r
456 \r
457         void resetSubscription(int subid) {\r
458             for (String nodename : BaseServlet.getNodes()) {\r
459                 String url = String.format(URL_TEMPLATE, nodename, subid);\r
460                 urls.add(url);\r
461             }\r
462         }\r
463 \r
464         @Override\r
465         public void run() {\r
466             try {\r
467                 while (!urls.isEmpty()) {\r
468                     String url = urls.remove(0);\r
469                     forceGetThrough(url);\r
470                 }\r
471             } catch (Exception e) {\r
472                 intlogger.warn("PROV0195 Caught exception in SubscriberNotifyThread: " + e.getMessage(), e);\r
473             }\r
474         }\r
475 \r
476         private void forceGetThrough(String url) {\r
477             try {\r
478                 URL urlObj = new URL(url);\r
479                 HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();\r
480                 conn.connect();\r
481                 conn.getContentLength();    // Force the GET through\r
482                 conn.disconnect();\r
483             } catch (IOException e) {\r
484                 intlogger.info("PROV0194 Error accessing URL: " + url + ": " + e.getMessage(), e);\r
485             }\r
486         }\r
487     }\r
488 }\r