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